import base64
import hashlib
import json
import os
import sys
from unittest import SkipTest, mock
from uuid import uuid4

import boto3
import pytest
from botocore.exceptions import ClientError
from freezegun import freeze_time

from moto import mock_aws, settings
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from moto.utilities.distutils_version import LooseVersion
from tests.test_ecr.test_ecr_helpers import _create_image_manifest
from tests.test_s3 import s3_aws_verified

from . import lambda_aws_verified
from .utilities import (
    PYTHON_VERSION,
    _process_lambda,
    create_invalid_lambda,
    get_role_name,
    get_test_zip_file1,
    get_test_zip_file2,
)

LAMBDA_FUNC_NAME = "test"
_lambda_region = "us-west-2"

boto3_version = sys.modules["botocore"].__version__


@mock.patch.dict("os.environ", {"MOTO_ENABLE_ISO_REGIONS": "true"})
@pytest.mark.parametrize("region", ["us-west-2", "cn-northwest-1", "us-isob-east-1"])
@mock_aws
def test_lambda_regions(region):
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest("Can only set EnvironVars in DecoratorMode")
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("ISO-region not available in older versions")
    client = boto3.client("lambda", region_name=region)
    resp = client.list_functions()
    assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200

    function_name = "moto_test_" + str(uuid4())[0:6]
    function = client.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(region),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": get_test_zip_file1()},
    )
    if region == "us-west-2":
        assert (
            function["FunctionArn"]
            == f"arn:aws:lambda:{region}:{ACCOUNT_ID}:function:{function_name}"
        )
    if region == "cn-northwest-1":
        assert (
            function["FunctionArn"]
            == f"arn:aws-cn:lambda:{region}:{ACCOUNT_ID}:function:{function_name}"
        )
    if region == "us-isob-east-1":
        assert (
            function["FunctionArn"]
            == f"arn:aws-iso-b:lambda:{region}:{ACCOUNT_ID}:function:{function_name}"
        )


@pytest.mark.aws_verified
@lambda_aws_verified
def test_list_functions(iam_role_arn=None):
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    sts = boto3.client("sts", "eu-west-2")
    account_id = sts.get_caller_identity()["Account"]

    conn = boto3.client("lambda", _lambda_region)
    function_name = "moto_test_" + str(uuid4())[0:6]
    initial_list = conn.list_functions()["Functions"]
    initial_names = [f["FunctionName"] for f in initial_list]
    assert function_name not in initial_names

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=iam_role_arn,
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": get_test_zip_file1()},
    )

    wait_for_func = conn.get_waiter("function_active")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    names = [f["FunctionName"] for f in conn.list_functions()["Functions"]]
    assert function_name in names

    conn.publish_version(FunctionName=function_name, Description="v2")
    func_list = conn.list_functions()["Functions"]
    our_functions = [f for f in func_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 1
    if LooseVersion(boto3_version) > LooseVersion("1.29.0"):
        # PackageType only available in new versions
        assert our_functions[0]["PackageType"] == "Zip"

    # FunctionVersion=ALL means we should get a list of all versions
    full_list = conn.list_functions(FunctionVersion="ALL")["Functions"]
    our_functions = [f for f in full_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 2
    for func in our_functions:
        assert func["PackageType"] == "Zip"

    v1 = [f for f in our_functions if f["Version"] == "1"][0]
    assert v1["Description"] == "v2"
    assert (
        v1["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{account_id}:function:{function_name}:1"
    )

    latest = [f for f in our_functions if f["Version"] == "$LATEST"][0]
    assert latest["Description"] == ""
    assert (
        latest["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{account_id}:function:{function_name}:$LATEST"
    )

    conn.delete_function(FunctionName=function_name)


@mock_aws
def test_create_based_on_s3_with_missing_bucket():
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    with pytest.raises(ClientError) as exc:
        conn.create_function(
            FunctionName=function_name,
            Runtime=PYTHON_VERSION,
            Role=get_role_name(),
            Handler="lambda_function.lambda_handler",
            Code={"S3Bucket": "this-bucket-does-not-exist", "S3Key": "test.zip"},
            Description="test lambda function",
            Timeout=3,
            MemorySize=128,
            Publish=True,
            VpcConfig={
                "SecurityGroupIds": ["sg-123abc"],
                "SubnetIds": ["subnet-123abc"],
            },
        )
    err = exc.value.response["Error"]
    assert (
        err["Message"]
        == "Error occurred while GetObject. S3 Error Code: NoSuchBucket. S3 Error Message: The specified bucket does not exist"
    )


@mock_aws
@freeze_time("2015-01-01 00:00:00")
def test_create_function_from_aws_bucket():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )
    zip_content = get_test_zip_file2()

    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    result = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        PackageType="ZIP",
        Publish=True,
        VpcConfig={"SecurityGroupIds": ["sg-123abc"], "SubnetIds": ["subnet-123abc"]},
    )

    assert result["FunctionName"] == function_name
    assert (
        result["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
    )
    assert result["Runtime"] == PYTHON_VERSION
    assert result["Handler"] == "lambda_function.lambda_handler"
    assert result["CodeSha256"] == base64.b64encode(
        hashlib.sha256(zip_content).digest()
    ).decode("utf-8")
    assert result["State"] == "Active"


@mock_aws
@freeze_time("2015-01-01 00:00:00")
def test_create_function_from_zipfile():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    result = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
    # this is hard to match against, so remove it
    result["ResponseMetadata"].pop("HTTPHeaders", None)
    # Botocore inserts retry attempts not seen in Python27
    result["ResponseMetadata"].pop("RetryAttempts", None)
    result["ResponseMetadata"].pop("RequestId")
    result.pop("LastModified")

    assert result == {
        "Architectures": ["x86_64"],
        "EphemeralStorage": {"Size": 512},
        "FunctionName": function_name,
        "FunctionArn": f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}",
        "Runtime": PYTHON_VERSION,
        "Role": result["Role"],
        "Handler": "lambda_function.lambda_handler",
        "CodeSize": len(zip_content),
        "Description": "test lambda function",
        "Timeout": 3,
        "MemorySize": 128,
        "PackageType": "Zip",
        "CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
            "utf-8"
        ),
        "Version": "1",
        "ResponseMetadata": {"HTTPStatusCode": 201},
        "State": "Active",
        "Layers": [],
        "TracingConfig": {"Mode": "PassThrough"},
        "SnapStart": {"ApplyOn": "None", "OptimizationStatus": "Off"},
    }


@mock_aws
def test_create_function_from_image():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
    image_config = {
        "EntryPoint": [
            "python",
        ],
        "Command": [
            "/opt/app.py",
        ],
        "WorkingDirectory": "/opt",
    }
    conn.create_function(
        FunctionName=function_name,
        Role=get_role_name(),
        Code={"ImageUri": image_uri},
        Description="test lambda function",
        ImageConfig=image_config,
        PackageType="Image",
    )

    result = conn.get_function(FunctionName=function_name)

    assert "ImageConfigResponse" in result["Configuration"]
    assert result["Configuration"]["ImageConfigResponse"]["ImageConfig"] == image_config


@mock_aws
def test_create_function_from_image_default_working_directory():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
    image_config = {
        "EntryPoint": [
            "python",
        ],
        "Command": [
            "/opt/app.py",
        ],
    }
    conn.create_function(
        FunctionName=function_name,
        Role=get_role_name(),
        Code={"ImageUri": image_uri},
        Description="test lambda function",
        ImageConfig=image_config,
        PackageType="Image",
    )

    result = conn.get_function(FunctionName=function_name)

    assert "ImageConfigResponse" in result["Configuration"]
    assert result["Configuration"]["ImageConfigResponse"]["ImageConfig"] == image_config


@mock_aws
def test_create_function_error_bad_architecture():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"

    with pytest.raises(ClientError) as exc:
        conn.create_function(
            Architectures=["foo"],
            FunctionName=function_name,
            Role=get_role_name(),
            Code={"ImageUri": image_uri},
        )

    err = exc.value.response

    assert err["Error"]["Code"] == "ValidationException"
    assert (
        err["Error"]["Message"]
        == "1 validation error detected: Value '['foo']' at 'architectures' failed to satisfy"
        " constraint: Member must satisfy constraint: [Member must satisfy enum value set: "
        "[x86_64, arm64], Member must not be null]"
    )


@mock_aws
def test_create_function_error_ephemeral_too_big():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"

    with pytest.raises(ClientError) as exc:
        conn.create_function(
            FunctionName=function_name,
            Role=get_role_name(),
            Code={"ImageUri": image_uri},
            Description="test lambda function",
            Timeout=3,
            MemorySize=128,
            Publish=True,
            EphemeralStorage={"Size": 3000000},
        )

    err = exc.value.response

    assert err["Error"]["Code"] == "ValidationException"
    assert (
        err["Error"]["Message"]
        == "1 validation error detected: Value '3000000' at 'ephemeralStorage.size' "
        "failed to satisfy constraint: "
        "Member must have value less than or equal to 10240"
    )


@mock_aws
@pytest.mark.parametrize(
    "tracing_mode",
    [(None, "PassThrough"), ("PassThrough", "PassThrough"), ("Active", "Active")],
)
def test_create_function__with_tracingmode(tracing_mode):
    conn = boto3.client("lambda", _lambda_region)
    source, output = tracing_mode
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    kwargs = {
        "FunctionName": function_name,
        "Runtime": PYTHON_VERSION,
        "Role": get_role_name(),
        "Handler": "lambda_function.lambda_handler",
        "Code": {"ZipFile": zip_content},
        "Description": "test lambda function",
        "Timeout": 3,
        "MemorySize": 128,
        "Publish": True,
    }
    if source:
        kwargs["TracingConfig"] = {"Mode": source}
    result = conn.create_function(**kwargs)
    assert result["TracingConfig"] == {"Mode": output}


@pytest.fixture(name="with_ecr_mock")
def ecr_repo_fixture():
    with mock_aws():
        os.environ["MOTO_LAMBDA_STUB_ECR"] = "FALSE"
        repo_name = "testlambdaecr"
        ecr_client = boto3.client("ecr", "us-east-1")
        ecr_client.create_repository(repositoryName=repo_name)
        response = ecr_client.put_image(
            repositoryName=repo_name,
            imageManifest=json.dumps(_create_image_manifest()),
            imageTag="latest",
        )
        yield response["image"]["imageId"]
        ecr_client.delete_repository(repositoryName=repo_name, force=True)
        os.environ["MOTO_LAMBDA_STUB_ECR"] = "TRUE"


@mock_aws
def test_create_function_from_stubbed_ecr():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    lambda_client = boto3.client("lambda", "us-east-1")
    fn_name = str(uuid4())[0:6]
    image_uri = "111122223333.dkr.ecr.us-east-1.amazonaws.com/testlambda:latest"

    dic = {
        "FunctionName": fn_name,
        "Role": get_role_name(),
        "Code": {"ImageUri": image_uri},
        "PackageType": "Image",
        "Timeout": 100,
    }

    resp = lambda_client.create_function(**dic)

    assert resp["FunctionName"] == fn_name
    assert resp["CodeSize"] == 0
    assert "CodeSha256" in resp
    assert resp["PackageType"] == "Image"

    result = lambda_client.get_function(FunctionName=fn_name)
    assert "Configuration" in result
    config = result["Configuration"]
    assert "Code" in result
    code = result["Code"]
    assert code["RepositoryType"] == "ECR"
    assert code["ImageUri"] == image_uri
    image_uri_without_tag = image_uri.split(":")[0]
    resolved_image_uri = f"{image_uri_without_tag}@sha256:{config['CodeSha256']}"
    assert code["ResolvedImageUri"] == resolved_image_uri


@mock_aws
def test_create_function_from_mocked_ecr_image_tag(
    with_ecr_mock,
):
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )

    lambda_client = boto3.client("lambda", "us-east-1")
    fn_name = str(uuid4())[0:6]
    image = with_ecr_mock
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:{image['imageTag']}"

    dic = {
        "FunctionName": fn_name,
        "Role": get_role_name(),
        "Code": {"ImageUri": image_uri},
        "PackageType": "Image",
        "Timeout": 100,
    }
    resp = lambda_client.create_function(**dic)

    assert resp["FunctionName"] == fn_name
    assert resp["CodeSize"] > 0
    assert "CodeSha256" in resp
    assert resp["PackageType"] == "Image"

    result = lambda_client.get_function(FunctionName=fn_name)
    assert "Configuration" in result
    config = result["Configuration"]
    assert config["CodeSha256"] == image["imageDigest"].replace("sha256:", "")
    assert config["CodeSize"] == resp["CodeSize"]
    assert "Code" in result
    code = result["Code"]
    assert code["RepositoryType"] == "ECR"
    assert code["ImageUri"] == image_uri
    image_uri_without_tag = image_uri.split(":")[0]
    resolved_image_uri = f"{image_uri_without_tag}@sha256:{config['CodeSha256']}"
    assert code["ResolvedImageUri"] == resolved_image_uri


@mock_aws
def test_create_function_from_mocked_ecr_image_digest(
    with_ecr_mock,
):
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )
    lambda_client = boto3.client("lambda", "us-east-1")
    fn_name = str(uuid4())[0:6]
    image = with_ecr_mock
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr@{image['imageDigest']}"

    dic = {
        "FunctionName": fn_name,
        "Role": get_role_name(),
        "Code": {"ImageUri": image_uri},
        "PackageType": "Image",
        "Timeout": 100,
    }
    resp = lambda_client.create_function(**dic)
    assert resp["FunctionName"] == fn_name
    assert resp["CodeSize"] > 0
    assert resp["CodeSha256"] == image["imageDigest"].replace("sha256:", "")
    assert resp["PackageType"] == "Image"


@mock_aws
def test_create_function_from_mocked_ecr_missing_image(
    with_ecr_mock,
):
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )

    lambda_client = boto3.client("lambda", "us-east-1")

    fn_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:dne"

    dic = {
        "FunctionName": fn_name,
        "Role": get_role_name(),
        "Code": {"ImageUri": image_uri},
        "PackageType": "Image",
        "Timeout": 100,
    }

    with pytest.raises(ClientError) as exc:
        lambda_client.create_function(**dic)

    err = exc.value.response["Error"]
    assert err["Code"] == "ImageNotFoundException"
    assert (
        err["Message"]
        == "The image with imageId {'imageTag': 'dne'} does not exist within the repository with name 'testlambdaecr' in the registry with id '123456789012'"
    )


@pytest.mark.aws_verified
@lambda_aws_verified
@s3_aws_verified
def test_get_function(iam_role_arn=None, bucket_name=None):
    region = "us-east-1"

    sts = boto3.client("sts", region)
    account_id = sts.get_caller_identity()["Account"]

    s3_conn = boto3.client("s3", region)

    zip_content = get_test_zip_file1()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=iam_role_arn,
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
        Environment={"Variables": {"test_variable": "test_value"}},
    )

    result = conn.get_function(FunctionName=function_name)

    assert result["Code"]["RepositoryType"] == "S3"

    assert result["Configuration"]["CodeSha256"] == base64.b64encode(
        hashlib.sha256(zip_content).digest()
    ).decode("utf-8")
    assert result["Configuration"]["CodeSize"] == len(zip_content)
    assert result["Configuration"]["Description"] == "test lambda function"
    assert "FunctionArn" in result["Configuration"]
    assert result["Configuration"]["FunctionName"] == function_name
    assert result["Configuration"]["Handler"] == "lambda_function.lambda_handler"
    assert result["Configuration"]["MemorySize"] == 128
    assert result["Configuration"]["Role"] == iam_role_arn
    assert result["Configuration"]["Runtime"] == PYTHON_VERSION
    assert result["Configuration"]["Timeout"] == 3
    assert result["Configuration"]["Version"] == "$LATEST"
    assert "VpcConfig" not in result["Configuration"]
    assert result["Configuration"]["Environment"]["Variables"] == {
        "test_variable": "test_value"
    }

    # Test get function with qualifier
    result = conn.get_function(FunctionName=function_name, Qualifier="$LATEST")
    assert result["Configuration"]["Version"] == "$LATEST"
    assert (
        result["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:{region}:{account_id}:function:{function_name}:$LATEST"
    )

    # Test get function with version
    result = conn.get_function(FunctionName=function_name, Qualifier="1")
    assert result["Configuration"]["Version"] == "1"
    assert (
        result["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:{region}:{account_id}:function:{function_name}:1"
    )

    # Test get function with version inside of name
    result = conn.get_function(FunctionName=f"{function_name}:1")
    assert result["Configuration"]["Version"] == "1"
    assert (
        result["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:{region}:{account_id}:function:{function_name}:1"
    )

    conn.delete_function(FunctionName=function_name)


@mock_aws
def test_get_unknown_function():
    conn = boto3.client("lambda", _lambda_region)

    # Test get function when can't find function name
    with pytest.raises(conn.exceptions.ResourceNotFoundException):
        conn.get_function(FunctionName="junk", Qualifier="$LATEST")


@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
@mock_aws
@freeze_time("2015-01-01 00:00:00")
def test_get_function_configuration(key):
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file1()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    fxn = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
        Environment={"Variables": {"test_variable": "test_value"}},
    )
    name_or_arn = fxn[key]

    result = conn.get_function_configuration(FunctionName=name_or_arn)

    assert result["CodeSha256"] == base64.b64encode(
        hashlib.sha256(zip_content).digest()
    ).decode("utf-8")
    assert result["CodeSize"] == len(zip_content)
    assert result["Description"] == "test lambda function"
    assert "FunctionArn" in result
    assert result["FunctionName"] == function_name
    assert result["Handler"] == "lambda_function.lambda_handler"
    assert result["MemorySize"] == 128
    assert result["Role"] == get_role_name()
    assert result["Runtime"] == PYTHON_VERSION
    assert result["Timeout"] == 3
    assert result["Version"] == "$LATEST"
    assert result["Environment"]["Variables"] == {"test_variable": "test_value"}

    # Test get function with qualifier
    result = conn.get_function_configuration(
        FunctionName=name_or_arn, Qualifier="$LATEST"
    )
    assert result["Version"] == "$LATEST"
    assert (
        result["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:$LATEST"
    )

    # Test get function when can't find function name
    with pytest.raises(conn.exceptions.ResourceNotFoundException):
        conn.get_function_configuration(FunctionName="junk", Qualifier="$LATEST")


@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
@mock_aws
def test_get_function_code_signing_config(key):
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file1()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    fxn = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        CodeSigningConfigArn="csc:arn",
    )
    name_or_arn = fxn[key]

    result = conn.get_function_code_signing_config(FunctionName=name_or_arn)

    assert result["FunctionName"] == function_name
    assert result["CodeSigningConfigArn"] == "csc:arn"


@mock_aws
def test_get_function_by_arn():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", "us-east-1")
    s3_conn.create_bucket(Bucket=bucket_name)

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", "us-east-1")
    function_name = str(uuid4())[0:6]

    fnc = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    result = conn.get_function(FunctionName=fnc["FunctionArn"])
    assert result["Configuration"]["FunctionName"] == function_name

    # Test with version
    result = conn.get_function(FunctionName=fnc["FunctionArn"], Qualifier="1")
    assert (
        result["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:{function_name}:1"
    )
    assert result["Configuration"]["Version"] == "1"

    # Test with version inside of ARN
    result = conn.get_function(FunctionName=f"{fnc['FunctionArn']}:1")
    assert result["Configuration"]["Version"] == "1"
    assert (
        result["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:{function_name}:1"
    )


@mock_aws
def test_delete_function():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    result = conn.delete_function(FunctionName=function_name)
    # There are differences in the modelled response between versions of Botocore,
    # so we check multiple places for the status code and allow for a success range.
    status_code = result.get(
        "StatusCode", result.get("ResponseMetadata", {}).get("HTTPStatusCode")
    )
    assert 200 <= status_code < 300

    func_list = conn.list_functions(FunctionVersion="ALL")["Functions"]
    our_functions = [f for f in func_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 0


@mock_aws
def test_delete_function_by_arn():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", "us-east-1")
    s3_conn.create_bucket(Bucket=bucket_name)

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", "us-east-1")
    function_name = str(uuid4())[0:6]

    fnc = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    conn.delete_function(FunctionName=fnc["FunctionArn"])

    func_list = conn.list_functions()["Functions"]
    our_functions = [f for f in func_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 0


@mock_aws
def test_delete_unknown_function():
    conn = boto3.client("lambda", _lambda_region)
    with pytest.raises(ClientError) as exc:
        conn.delete_function(FunctionName="testFunctionThatDoesntExist")
    err = exc.value.response["Error"]
    assert err["Code"] == "ResourceNotFoundException"


@mock_aws
@pytest.mark.parametrize(
    "name",
    [
        "bad_function_name",
        f"arn:aws:lambda:eu-west-1:{ACCOUNT_ID}:function:bad_function_name",
    ],
)
def test_publish_version_unknown_function(name):
    client = boto3.client("lambda", "eu-west-1")
    with pytest.raises(ClientError) as exc:
        client.publish_version(FunctionName=name, Description="v2")
    err = exc.value.response["Error"]
    assert err["Code"] == "ResourceNotFoundException"
    assert (
        err["Message"]
        == f"Function not found: arn:aws:lambda:eu-west-1:{ACCOUNT_ID}:function:bad_function_name"
    )


@mock_aws
def test_publish():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=False,
    )

    function_list = conn.list_functions(FunctionVersion="ALL")["Functions"]
    our_functions = [f for f in function_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 1
    latest_arn = our_functions[0]["FunctionArn"]

    res = conn.publish_version(FunctionName=function_name)
    assert res["ResponseMetadata"]["HTTPStatusCode"] == 201

    function_list = conn.list_functions(FunctionVersion="ALL")["Functions"]
    our_functions = [f for f in function_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 2

    # #SetComprehension ;-)
    published_arn = list({f["FunctionArn"] for f in our_functions} - {latest_arn})[0]
    assert f"{function_name}:1" in published_arn

    conn.delete_function(FunctionName=function_name, Qualifier="1")

    function_list = conn.list_functions()["Functions"]
    our_functions = [f for f in function_list if f["FunctionName"] == function_name]
    assert len(our_functions) == 1
    assert function_name in our_functions[0]["FunctionArn"]


@mock_aws
@freeze_time("2015-01-01 00:00:00")
def test_list_create_list_get_delete_list():
    """
    test `list -> create -> list -> get -> delete -> list` integration

    """
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    initial_list = conn.list_functions()["Functions"]
    initial_names = [f["FunctionName"] for f in initial_list]
    assert function_name not in initial_names

    function_name = function_name
    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        EphemeralStorage={"Size": 2500},
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
    expected_function_result = {
        "Code": {
            "Location": f"https://awslambda-{_lambda_region}-tasks.s3.{_lambda_region}.amazonaws.com/test.zip",
            "RepositoryType": "S3",
        },
        "Configuration": {
            "CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
                "utf-8"
            ),
            "CodeSize": len(zip_content),
            "Description": "test lambda function",
            "FunctionName": function_name,
            "Handler": "lambda_function.lambda_handler",
            "MemorySize": 128,
            "PackageType": "Zip",
            "Role": get_role_name(),
            "Runtime": PYTHON_VERSION,
            "Timeout": 3,
            "Version": "$LATEST",
            "State": "Active",
            "Layers": [],
            "LastUpdateStatus": "Successful",
            "TracingConfig": {"Mode": "PassThrough"},
            "Architectures": ["x86_64"],
            "EphemeralStorage": {"Size": 2500},
            "SnapStart": {"ApplyOn": "None", "OptimizationStatus": "Off"},
        },
        "ResponseMetadata": {"HTTPStatusCode": 200},
    }
    functions = conn.list_functions()["Functions"]
    func_names = [f["FunctionName"] for f in functions]
    assert function_name in func_names

    func_arn = [
        f["FunctionArn"] for f in functions if f["FunctionName"] == function_name
    ][0]
    assert (
        func_arn
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
    )
    functions = conn.list_functions(FunctionVersion="ALL")["Functions"]
    our_functions = [f for f in functions if f["FunctionName"] == function_name]
    assert len(our_functions) == 2

    latest = [f for f in our_functions if f["Version"] == "$LATEST"][0]
    assert (
        latest["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:$LATEST"
    )
    latest.pop("FunctionArn")
    latest.pop("LastModified")
    assert latest == expected_function_result["Configuration"]

    published = [f for f in our_functions if f["Version"] != "$LATEST"][0]
    assert published["Version"] == "1"
    assert (
        published["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:1"
    )

    func = conn.get_function(FunctionName=function_name)

    assert (
        func["Configuration"]["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
    )

    # this is hard to match against, so remove it
    func["ResponseMetadata"].pop("HTTPHeaders", None)
    # Botocore inserts retry attempts not seen in Python27
    func["ResponseMetadata"].pop("RetryAttempts", None)
    func["ResponseMetadata"].pop("RequestId")
    func["Configuration"].pop("LastModified")
    func["Configuration"].pop("FunctionArn")

    assert func == expected_function_result
    conn.delete_function(FunctionName=function_name)

    functions = conn.list_functions()["Functions"]
    func_names = [f["FunctionName"] for f in functions]
    assert function_name not in func_names


@mock_aws
@freeze_time("2015-01-01 00:00:00")
def test_get_function_created_with_zipfile():
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    zip_content = get_test_zip_file1()
    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
    )

    response = conn.get_function(FunctionName=function_name)

    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert len(response["Code"]) == 2
    assert response["Code"]["RepositoryType"] == "S3"
    assert response["Code"]["Location"].startswith(
        f"https://awslambda-{_lambda_region}-tasks.s3.{_lambda_region}.amazonaws.com"
    )
    assert "Configuration" in response
    config = response["Configuration"]
    assert config["CodeSha256"] == base64.b64encode(
        hashlib.sha256(zip_content).digest()
    ).decode("utf-8")
    assert config["CodeSize"] == len(zip_content)
    assert config["Description"] == "test lambda function"
    assert (
        config["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
    )
    assert config["FunctionName"] == function_name
    assert config["Handler"] == "lambda_function.handler"
    assert config["MemorySize"] == 128
    assert config["Role"] == get_role_name()
    assert config["Runtime"] == PYTHON_VERSION
    assert config["Timeout"] == 3
    assert config["Version"] == "$LATEST"
    assert config["State"] == "Active"
    assert config["Layers"] == []
    assert config["LastUpdateStatus"] == "Successful"


@mock_aws
def test_list_versions_by_function():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
    conn.update_function_code(FunctionName=function_name, ZipFile=get_test_zip_file1())

    res = conn.publish_version(FunctionName=function_name)
    assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
    versions = conn.list_versions_by_function(FunctionName=function_name)
    assert len(versions["Versions"]) == 3
    assert (
        versions["Versions"][0]["FunctionArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:$LATEST"
    )
    assert (
        versions["Versions"][1]["FunctionArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:1"
    )
    assert (
        versions["Versions"][2]["FunctionArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:2"
    )

    conn.create_function(
        FunctionName="testFunction_2",
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=False,
    )
    versions = conn.list_versions_by_function(FunctionName="testFunction_2")
    assert len(versions["Versions"]) == 1
    assert (
        versions["Versions"][0]["FunctionArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:testFunction_2:$LATEST"
    )


@mock_aws
def test_list_aliases():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    function_name2 = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    conn.create_function(
        FunctionName=function_name2,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    first_version = conn.publish_version(FunctionName=function_name)["Version"]

    conn.create_alias(
        FunctionName=function_name,
        Name="alias1",
        FunctionVersion=first_version,
    )

    conn.update_function_code(FunctionName=function_name, ZipFile=get_test_zip_file1())
    second_version = conn.publish_version(FunctionName=function_name)["Version"]

    conn.create_alias(
        FunctionName=function_name,
        Name="alias2",
        FunctionVersion=second_version,
    )

    conn.create_alias(
        FunctionName=function_name,
        Name="alias0",
        FunctionVersion=second_version,
    )

    aliases = conn.list_aliases(FunctionName=function_name)
    assert len(aliases["Aliases"]) == 3

    # should be ordered by their alias name (as per SDK response)
    assert (
        aliases["Aliases"][0]["AliasArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:alias0"
    )
    assert aliases["Aliases"][0]["FunctionVersion"] == second_version

    assert (
        aliases["Aliases"][1]["AliasArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:alias1"
    )
    assert aliases["Aliases"][1]["FunctionVersion"] == first_version

    assert (
        aliases["Aliases"][2]["AliasArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name}:alias2"
    )
    assert aliases["Aliases"][2]["FunctionVersion"] == second_version

    res = conn.publish_version(FunctionName=function_name2)
    conn.create_alias(
        FunctionName=function_name2,
        Name="alias1",
        FunctionVersion=res["Version"],
    )

    aliases = conn.list_aliases(FunctionName=function_name2)

    assert len(aliases["Aliases"]) == 1
    assert (
        aliases["Aliases"][0]["AliasArn"]
        == f"arn:aws:lambda:us-west-2:{ACCOUNT_ID}:function:{function_name2}:alias1"
    )


@mock_aws
def test_create_function_with_already_exists():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    with pytest.raises(ClientError) as exc:
        conn.create_function(
            FunctionName=function_name,
            Runtime=PYTHON_VERSION,
            Role=get_role_name(),
            Handler="lambda_function.lambda_handler",
            Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
            Description="test lambda function",
            Timeout=3,
            MemorySize=128,
            Publish=True,
        )

    assert exc.value.response["Error"]["Code"] == "ResourceConflictException"


@mock_aws
def test_list_versions_by_function_for_nonexistent_function():
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    versions = conn.list_versions_by_function(FunctionName=function_name)

    assert len(versions["Versions"]) == 0


@pytest.mark.aws_verified
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
@lambda_aws_verified
@s3_aws_verified
def test_update_configuration(key, bucket_name=None, iam_role_arn=None):
    region = "us-east-1"
    function_name = str(uuid4())[0:6]
    s3_conn = boto3.client("s3", region)

    zip_content = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
    conn = boto3.client("lambda", region)

    fxn = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=iam_role_arn,
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
        Environment={"Variables": {"test_old_environment": "test_old_value"}},
    )
    name_or_arn = fxn[key]

    wait_for_func = conn.get_waiter("function_active")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    assert fxn["Description"] == "test lambda function"
    assert fxn["Handler"] == "lambda_function.lambda_handler"
    assert fxn["MemorySize"] == 128
    assert fxn["Runtime"] == PYTHON_VERSION
    assert fxn["Timeout"] == 3

    updated_config = conn.update_function_configuration(
        FunctionName=name_or_arn,
        Description="updated test lambda function",
        Handler="lambda_function.new_lambda_handler",
        Runtime="python3.12",
        Timeout=7,
        Environment={"Variables": {"test_environment": "test_value"}},
    )

    wait_for_func = conn.get_waiter("function_updated")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    assert updated_config["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert updated_config["Description"] == "updated test lambda function"
    assert updated_config["Handler"] == "lambda_function.new_lambda_handler"
    assert updated_config["MemorySize"] == 128
    assert updated_config["Runtime"] == "python3.12"
    assert updated_config["Timeout"] == 7
    assert updated_config["Environment"]["Variables"] == {
        "test_environment": "test_value"
    }

    # publish - results in a new version because the config is updated
    assert conn.publish_version(FunctionName=name_or_arn)["Version"] == "2"

    # publish again - no changes
    assert conn.publish_version(FunctionName=name_or_arn)["Version"] == "2"

    conn.delete_function(FunctionName=function_name)


@pytest.mark.aws_verified
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
@lambda_aws_verified
def test_update_function_zip(key, iam_role_arn=None):
    conn = boto3.client("lambda", _lambda_region)

    zip_content_one = get_test_zip_file1()
    function_name = str(uuid4())[0:6]

    fxn = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=iam_role_arn,
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content_one},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
    name_or_arn = fxn[key]

    wait_for_func = conn.get_waiter("function_active")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    first_sha = fxn["CodeSha256"]

    zip_content_two = get_test_zip_file2()

    update1 = conn.update_function_code(
        FunctionName=name_or_arn, ZipFile=zip_content_two, Publish=True
    )
    assert update1["CodeSha256"] != first_sha

    wait_for_func = conn.get_waiter("function_updated")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    response = conn.get_function(FunctionName=function_name, Qualifier="2")

    assert response["Code"]["RepositoryType"] == "S3"
    assert response["Code"]["Location"].startswith(
        f"https://awslambda-{_lambda_region}-tasks.s3.{_lambda_region}.amazonaws.com"
    )

    config = response["Configuration"]
    assert config["CodeSize"] == len(zip_content_two)
    assert config["FunctionArn"].endswith(f"{function_name}:2")
    assert config["FunctionName"] == function_name
    assert config["Version"] == "2"
    assert config["CodeSha256"] == update1["CodeSha256"]

    most_recent_config = conn.get_function(FunctionName=function_name)
    assert most_recent_config["Configuration"]["CodeSha256"] == update1["CodeSha256"]

    # Update the same code, and manually publish
    conn.update_function_code(FunctionName=name_or_arn, ZipFile=zip_content_two)
    wait_for_func = conn.get_waiter("function_updated")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    resp = conn.publish_version(FunctionName=name_or_arn)
    # The code is the same, but the version is still updated
    assert resp["CodeSha256"] == update1["CodeSha256"]
    assert resp["Version"] == "3"

    # Calling publish again
    resp = conn.publish_version(FunctionName=name_or_arn)
    # Code is the same, as is the version - just because we didn't call 'update_function_code'
    assert resp["CodeSha256"] == update1["CodeSha256"]
    assert resp["Version"] == "3"

    # Explicitly publishing this again, with the same code, gives us a new version
    same_update = conn.update_function_code(
        FunctionName=name_or_arn, ZipFile=zip_content_two, Publish=True
    )
    assert same_update["FunctionArn"].endswith(f":{function_name}:4")
    assert same_update["Version"] == "4"

    wait_for_func = conn.get_waiter("function_updated")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    resp = conn.publish_version(FunctionName=name_or_arn)
    # The code is the same, so is the version, as the previous update was already published
    assert resp["CodeSha256"] == update1["CodeSha256"]
    assert resp["Version"] == "4"

    wait_for_func = conn.get_waiter("function_updated")
    wait_for_func.wait(FunctionName=function_name, WaiterConfig={"Delay": 1})

    conn.delete_function(FunctionName=function_name)


@mock_aws
def test_update_function_s3():
    bucket_name = str(uuid4())
    s3_conn = boto3.client("s3", _lambda_region)
    s3_conn.create_bucket(
        Bucket=bucket_name,
        CreateBucketConfiguration={"LocationConstraint": _lambda_region},
    )

    zip_content = get_test_zip_file1()
    s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)

    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]

    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    zip_content_two = get_test_zip_file2()
    s3_conn.put_object(Bucket=bucket_name, Key="test2.zip", Body=zip_content_two)

    conn.update_function_code(
        FunctionName=function_name,
        S3Bucket=bucket_name,
        S3Key="test2.zip",
        Publish=True,
    )

    response = conn.get_function(FunctionName=function_name, Qualifier="2")

    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert len(response["Code"]) == 2
    assert response["Code"]["RepositoryType"] == "S3"
    assert response["Code"]["Location"].startswith(
        f"https://awslambda-{_lambda_region}-tasks.s3.{_lambda_region}.amazonaws.com"
    )

    config = response["Configuration"]
    assert config["CodeSha256"] == base64.b64encode(
        hashlib.sha256(zip_content_two).digest()
    ).decode("utf-8")
    assert config["CodeSize"] == len(zip_content_two)
    assert config["Description"] == "test lambda function"
    assert (
        config["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:2"
    )
    assert config["FunctionName"] == function_name
    assert config["Version"] == "2"
    assert config["LastUpdateStatus"] == "Successful"


@mock_aws
def test_update_function_ecr():
    if LooseVersion(boto3_version) < LooseVersion("1.29.0"):
        raise SkipTest("Parameters only available in newer versions")
    conn = boto3.client("lambda", _lambda_region)
    function_name = str(uuid4())[0:6]
    image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
    image_config = {
        "EntryPoint": [
            "python",
        ],
        "Command": [
            "/opt/app.py",
        ],
        "WorkingDirectory": "/opt",
    }

    conn.create_function(
        FunctionName=function_name,
        Role=get_role_name(),
        Code={"ImageUri": image_uri},
        Description="test lambda function",
        ImageConfig=image_config,
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    new_uri = image_uri.replace("prod", "newer")

    conn.update_function_code(
        FunctionName=function_name,
        ImageUri=new_uri,
        Publish=True,
    )

    response = conn.get_function(FunctionName=function_name, Qualifier="2")

    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert len(response["Code"]) == 3
    assert response["Code"]["RepositoryType"] == "ECR"
    assert response["Code"]["ImageUri"] == new_uri
    assert response["Code"]["ResolvedImageUri"].endswith(
        hashlib.sha256(new_uri.encode("utf-8")).hexdigest()
    )

    config = response["Configuration"]
    assert config["CodeSize"] == 0
    assert config["Description"] == "test lambda function"
    assert (
        config["FunctionArn"]
        == f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:2"
    )
    assert config["FunctionName"] == function_name
    assert config["Version"] == "2"
    assert config["LastUpdateStatus"] == "Successful"


@mock_aws
def test_create_function_with_invalid_arn():
    err = create_invalid_lambda("test-iam-role")
    assert (
        err.value.response["Error"]["Message"]
        == r"1 validation error detected: Value 'test-iam-role' at 'role' failed to satisfy constraint: Member must satisfy regular expression pattern: arn:(aws[a-zA-Z-]*)?:iam::(\d{12}):role/?[a-zA-Z_0-9+=,.@\-_/]+"
    )


@mock_aws
def test_create_function_with_arn_from_different_account():
    err = create_invalid_lambda("arn:aws:iam::000000000000:role/example_role")
    assert (
        err.value.response["Error"]["Message"]
        == "Cross-account pass role is not allowed."
    )


@mock_aws
def test_create_function_with_unknown_arn():
    err = create_invalid_lambda(
        "arn:aws:iam::" + str(ACCOUNT_ID) + ":role/service-role/unknown_role"
    )
    assert (
        err.value.response["Error"]["Message"]
        == "The role defined for the function cannot be assumed by Lambda."
    )


@mock_aws
def test_remove_unknown_permission_throws_error():
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    f = conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=(get_role_name()),
        Handler="lambda_function.handler",
        Code={"ZipFile": zip_content},
    )
    arn = f["FunctionArn"]

    with pytest.raises(ClientError) as exc:
        conn.remove_permission(FunctionName=arn, StatementId="1")
    err = exc.value.response["Error"]
    assert err["Code"] == "ResourceNotFoundException"
    assert err["Message"] == "No policy is associated with the given resource."


@mock_aws
def test_multiple_qualifiers():
    client = boto3.client("lambda", "us-east-1")

    zip_content = get_test_zip_file1()
    fn_name = str(uuid4())[0:6]
    client.create_function(
        FunctionName=fn_name,
        Runtime=PYTHON_VERSION,
        Role=(get_role_name()),
        Handler="lambda_function.handler",
        Code={"ZipFile": zip_content},
    )

    for _ in range(10):
        new_zip = _process_lambda(f"func content {_}")
        client.update_function_code(FunctionName=fn_name, ZipFile=new_zip)
        client.publish_version(FunctionName=fn_name)

    resp = client.list_versions_by_function(FunctionName=fn_name)["Versions"]
    qualis = [fn["FunctionArn"].split(":")[-1] for fn in resp]
    assert qualis == ["$LATEST", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

    # Test delete with function name and qualifier
    client.delete_function(FunctionName=fn_name, Qualifier="4")
    # Test delete with ARN and qualifier
    client.delete_function(
        FunctionName=f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:{fn_name}",
        Qualifier="5",
    )
    # Test delete with qualifier part of function name
    client.delete_function(FunctionName=fn_name + ":8")
    # Test delete with qualifier inside ARN
    client.delete_function(
        FunctionName=f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:{fn_name}:9"
    )

    resp = client.list_versions_by_function(FunctionName=fn_name)["Versions"]
    qualis = [fn["FunctionArn"].split(":")[-1] for fn in resp]
    assert qualis == ["$LATEST", "1", "2", "3", "6", "7", "10"]

    fn = client.get_function(FunctionName=fn_name, Qualifier="6")["Configuration"]
    assert (
        fn["FunctionArn"]
        == f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:{fn_name}:6"
    )


@mock_aws
def test_delete_non_existent():
    client = boto3.client("lambda", "us-east-1")

    with pytest.raises(ClientError) as exc:
        client.delete_function(
            FunctionName=f"arn:aws:lambda:us-east-1:{ACCOUNT_ID}:function:nonexistent:9"
        )

    assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"


def test_get_role_name_utility_race_condition():
    # Play with these variables as needed to reproduce the error.
    max_workers, num_threads = 3, 15

    errors = []
    roles = []

    def thread_function(_):
        while True:
            # noinspection PyBroadException
            try:
                role = get_role_name()
            except ClientError as e:
                errors.append(str(e))
                break
            except Exception:
                # boto3 and our own IAMBackend are not thread-safe,
                # and occasionally throw weird errors, so we just
                # pass and retry.
                # https://github.com/boto/boto3/issues/1592
                pass
            else:
                roles.append(role)
                break

    import concurrent.futures

    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        executor.map(thread_function, range(num_threads))
    # Check all threads are accounted for, all roles are the same entity,
    # and there are no client errors.
    assert len(errors) + len(roles) == num_threads
    assert roles.count(roles[0]) == len(roles)
    assert len(errors) == 0


@mock_aws
@mock.patch.dict(os.environ, {"MOTO_LAMBDA_CONCURRENCY_QUOTA": "1000"})
def test_put_function_concurrency_success():
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    response = conn.put_function_concurrency(
        FunctionName=function_name, ReservedConcurrentExecutions=900
    )
    assert response["ReservedConcurrentExecutions"] == 900


@mock_aws
@mock.patch.dict(os.environ, {"MOTO_LAMBDA_CONCURRENCY_QUOTA": "don't care"})
def test_put_function_concurrency_not_enforced():
    del os.environ["MOTO_LAMBDA_CONCURRENCY_QUOTA"]  # i.e. not set by user
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    # This works, even though it normally would be disallowed by AWS
    response = conn.put_function_concurrency(
        FunctionName=function_name, ReservedConcurrentExecutions=901
    )
    assert response["ReservedConcurrentExecutions"] == 901


@mock_aws
@mock.patch.dict(os.environ, {"MOTO_LAMBDA_CONCURRENCY_QUOTA": "1000"})
def test_put_function_concurrency_failure():
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name = str(uuid4())[0:6]
    conn.create_function(
        FunctionName=function_name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    with pytest.raises(ClientError) as exc:
        conn.put_function_concurrency(
            FunctionName=function_name, ReservedConcurrentExecutions=901
        )

    assert exc.value.response["Error"]["Code"] == "InvalidParameterValueException"

    # No reservation should have been set
    response = conn.get_function_concurrency(FunctionName=function_name)
    assert "ReservedConcurrentExecutions" not in response


@mock_aws
@mock.patch.dict(os.environ, {"MOTO_LAMBDA_CONCURRENCY_QUOTA": "1000"})
def test_put_function_concurrency_i_can_has_math():
    if not settings.TEST_DECORATOR_MODE:
        raise SkipTest(
            "Envars not easily set in server mode, feature off by default, skipping..."
        )
    conn = boto3.client("lambda", _lambda_region)
    zip_content = get_test_zip_file1()
    function_name_1 = str(uuid4())[0:6]
    function_name_2 = str(uuid4())[0:6]
    conn.create_function(
        FunctionName=function_name_1,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
    conn.create_function(
        FunctionName=function_name_2,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )

    response = conn.put_function_concurrency(
        FunctionName=function_name_1, ReservedConcurrentExecutions=600
    )
    assert response["ReservedConcurrentExecutions"] == 600
    response = conn.put_function_concurrency(
        FunctionName=function_name_2, ReservedConcurrentExecutions=100
    )
    assert response["ReservedConcurrentExecutions"] == 100

    # Increasing function 1's limit should succeed, e.g. 700 + 100 <= 900
    response = conn.put_function_concurrency(
        FunctionName=function_name_1, ReservedConcurrentExecutions=700
    )
    assert response["ReservedConcurrentExecutions"] == 700

    # Increasing function 2's limit should fail, e.g. 700 + 201 > 900
    with pytest.raises(ClientError) as exc:
        conn.put_function_concurrency(
            FunctionName=function_name_2, ReservedConcurrentExecutions=201
        )

    assert exc.value.response["Error"]["Code"] == "InvalidParameterValueException"
    response = conn.get_function_concurrency(FunctionName=function_name_2)
    assert response["ReservedConcurrentExecutions"] == 100


@mock_aws
@pytest.mark.parametrize(
    "config",
    [
        {
            "OnSuccess": {
                "Destination": f"arn:aws:lambda:us-west-2:123456789012:function:{LAMBDA_FUNC_NAME}-2"
            },
            "OnFailure": {},
        },
        {
            "OnFailure": {
                "Destination": f"arn:aws:lambda:us-west-2:123456789012:function:{LAMBDA_FUNC_NAME}-2"
            },
            "OnSuccess": {},
        },
        {
            "OnFailure": {
                "Destination": "arn:aws:lambda:us-west-2:123456789012:function:test-2"
            },
            "OnSuccess": {
                "Destination": "arn:aws:lambda:us-west-2:123456789012:function:test-2"
            },
        },
    ],
)
def test_put_event_invoke_config(config):
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]

    # the name has to match ARNs in pytest parameterize
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]

    # Execute
    result = client.put_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
    )

    # Verify
    assert result["FunctionArn"] == arn_1
    assert result["DestinationConfig"] == config

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


@mock_aws
def test_put_event_invoke_config_without_config():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]

    # Execute
    result = client.put_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, MaximumRetryAttempts=2
    )

    # Verify
    assert result["MaximumRetryAttempts"] == 2

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)


@mock_aws
@pytest.mark.parametrize(
    "config",
    [
        {
            "OnSuccess": {
                "Destination": f"arn:aws:lambda:us-west-2:123456789012:function:{LAMBDA_FUNC_NAME}-2"
            },
            "OnFailure": {},
        },
        {
            "OnFailure": {
                "Destination": f"arn:aws:lambda:us-west-2:123456789012:function:{LAMBDA_FUNC_NAME}-2"
            },
            "OnSuccess": {},
        },
        {
            "OnFailure": {
                "Destination": "arn:aws:lambda:us-west-2:123456789012:function:test-2"
            },
            "OnSuccess": {
                "Destination": "arn:aws:lambda:us-west-2:123456789012:function:test-2"
            },
        },
    ],
)
def test_update_event_invoke_config(config):
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]

    # the name has to match ARNs in pytest parameterize
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]

    # Execute
    result = client.update_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
    )

    # Verify
    assert result["FunctionArn"] == arn_1
    assert result["DestinationConfig"] == config

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


@mock_aws
def test_put_event_invoke_config_errors_1():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    config = {
        "OnSuccess": {"Destination": "invalid"},
        "OnFailure": {},
    }

    # Execute
    with pytest.raises(ClientError) as exc:
        client.put_function_event_invoke_config(
            FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
        )

    # Verify
    err = exc.value.response["Error"]
    assert err["Code"] == "ValidationException"
    assert (
        err["Message"] == "1 validation error detected: "
        "Value 'invalid' at 'destinationConfig.onSuccess.destination' failed to satisfy constraint: "
        "Member must satisfy regular expression pattern: "
        r"^$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}(-gov)?-[a-z]+-\d{1})?:(\d{12})?:(.*)"
    )

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)


@mock_aws
def test_put_event_invoke_config_errors_2():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]
    config = {
        "OnFailure": {"Destination": "invalid"},
        "OnSuccess": {},
    }

    # Execute
    with pytest.raises(ClientError) as exc:
        client.put_function_event_invoke_config(
            FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
        )

    # Verify
    err = exc.value.response["Error"]
    assert err["Code"] == "ValidationException"
    assert (
        err["Message"] == "1 validation error detected: "
        "Value 'invalid' at 'destinationConfig.onFailure.destination' failed to satisfy constraint: "
        "Member must satisfy regular expression pattern: "
        r"^$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}(-gov)?-[a-z]+-\d{1})?:(\d{12})?:(.*)"
    )

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


@mock_aws
def test_put_event_invoke_config_errors_3():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    test_val = 5

    # Execute
    with pytest.raises(ClientError) as exc:
        client.put_function_event_invoke_config(
            FunctionName=LAMBDA_FUNC_NAME, MaximumRetryAttempts=test_val
        )

    # Verify
    err = exc.value.response["Error"]
    assert err["Code"] == "ValidationException"
    assert (
        err["Message"] == "1 validation error detected: "
        f"Value '{test_val}' at 'maximumRetryAttempts' failed to satisfy constraint: "
        "Member must have value less than or equal to 2"
    )

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)


@mock_aws
def test_put_event_invoke_config_errors_4():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    test_val = 44444

    # Execute
    with pytest.raises(ClientError) as exc:
        client.put_function_event_invoke_config(
            FunctionName=LAMBDA_FUNC_NAME, MaximumEventAgeInSeconds=test_val
        )

    # Verify
    err = exc.value.response["Error"]
    assert err["Code"] == "ValidationException"
    assert (
        err["Message"] == "1 validation error detected: "
        f"Value '{test_val}' at 'maximumEventAgeInSeconds' failed to satisfy constraint: "
        "Member must have value less than or equal to 21600"
    )

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)


@mock_aws
def test_get_event_invoke_config():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]
    config = {
        "OnFailure": {"Destination": arn_2},
        "OnSuccess": {"Destination": arn_2},
    }
    client.put_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
    )

    # Execute
    result = client.get_function_event_invoke_config(FunctionName=LAMBDA_FUNC_NAME)

    # Verify
    assert result["DestinationConfig"] == config
    assert result["FunctionArn"] == arn_1
    assert "LastModified" in result

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


@mock_aws
def test_list_event_invoke_configs():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]
    config = {
        "OnFailure": {"Destination": arn_2},
        "OnSuccess": {"Destination": arn_2},
    }

    # Execute
    result = client.list_function_event_invoke_configs(FunctionName=LAMBDA_FUNC_NAME)

    # Verify
    assert "FunctionEventInvokeConfigs" in result
    assert result["FunctionEventInvokeConfigs"] == []

    # Execute
    client.put_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
    )
    result = client.list_function_event_invoke_configs(FunctionName=LAMBDA_FUNC_NAME)

    # Verify
    assert len(result["FunctionEventInvokeConfigs"]) == 1
    assert result["FunctionEventInvokeConfigs"][0]["DestinationConfig"] == config

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


@mock_aws
def test_get_event_invoke_config_empty():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]

    # Execute
    with pytest.raises(ClientError) as exc:
        client.get_function_event_invoke_config(FunctionName=LAMBDA_FUNC_NAME)

    err = exc.value.response

    # Verify
    assert err["Error"]["Code"] == "ResourceNotFoundException"
    assert (
        err["Error"]["Message"]
        == f"The function {arn_1} doesn't have an EventInvokeConfig"
    )

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)


@mock_aws
def test_delete_event_invoke_config():
    # Setup
    client = boto3.client("lambda", _lambda_region)
    arn_1 = setup_lambda(client, LAMBDA_FUNC_NAME)["FunctionArn"]

    # the name has to match ARNs in pytest parameterize
    arn_2 = setup_lambda(client, f"{LAMBDA_FUNC_NAME}-2")["FunctionArn"]
    config = {"OnSuccess": {"Destination": arn_2}, "OnFailure": {}}
    client.put_function_event_invoke_config(
        FunctionName=LAMBDA_FUNC_NAME, DestinationConfig=config
    )

    # Execute
    result = client.delete_function_event_invoke_config(FunctionName=LAMBDA_FUNC_NAME)
    # Verify
    assert result["ResponseMetadata"]["HTTPStatusCode"] == 204

    # Clean up for servertests
    client.delete_function(FunctionName=arn_1)
    client.delete_function(FunctionName=arn_2)


def setup_lambda(client, name):
    zip_content = get_test_zip_file1()
    return client.create_function(
        FunctionName=name,
        Runtime=PYTHON_VERSION,
        Role=get_role_name(),
        Handler="lambda_function.lambda_handler",
        Code={"ZipFile": zip_content},
        Description="test lambda function",
        Timeout=3,
        MemorySize=128,
        Publish=True,
    )
