11"""Integration tests for AgentCoreToolSearchPlugin.
22
3- Requires environment variables:
3+ If GATEWAY_ROLE_ARN and GATEWAY_LAMBDA_ARN are set, uses those directly.
4+ Otherwise, automatically provisions the IAM role and Lambda function,
5+ and tears them down after the test run.
6+
7+ Environment variables (all optional):
48 BEDROCK_TEST_REGION: AWS region (default: us-west-2)
59 GATEWAY_ROLE_ARN: IAM role ARN with AgentCore gateway trust policy
6- GATEWAY_LAMBDA_ARN: Lambda ARN for the gateway target (must implement MCP tool handler)
7-
8- Prerequisites:
9- 1. Deploy the Lambda in tests_integ/gateway/integrations/lambda_function/lambda_function.py
10- (Python 3.10+ runtime, handler: lambda_function.lambda_handler)
11-
12- 2. The GATEWAY_ROLE_ARN must have:
13- - Trust policy for bedrock-agentcore.amazonaws.com
14- - lambda:InvokeFunction permission on the GATEWAY_LAMBDA_ARN
15-
16- Example inline policy:
17- {
18- "Version": "2012-10-17",
19- "Statement": [{
20- "Effect": "Allow",
21- "Action": "lambda:InvokeFunction",
22- "Resource": "<GATEWAY_LAMBDA_ARN>"
23- }]
24- }
25-
26- 3. Install mcp-proxy-for-aws: uv pip install mcp-proxy-for-aws
27-
10+ GATEWAY_LAMBDA_ARN: Lambda ARN for the gateway target
2811"""
2912
13+ import io
14+ import json
3015import logging
3116import os
3217import time
18+ import zipfile
3319
20+ import boto3
3421import pytest
3522
3623from bedrock_agentcore .gateway .client import GatewayClient
4330logging .basicConfig (level = logging .INFO , format = "%(asctime)s - %(levelname)s - %(message)s" )
4431logger = logging .getLogger (__name__ )
4532
33+ # Infrastructure constants
34+ _ROLE_NAME = "integ-test-gateway-role"
35+ _LAMBDA_NAME = "integ-test-lambda"
36+ _LAMBDA_HANDLER = "lambda_function.lambda_handler"
37+ _LAMBDA_RUNTIME = "python3.10"
38+
39+ _TRUST_POLICY = {
40+ "Version" : "2012-10-17" ,
41+ "Statement" : [
42+ {
43+ "Effect" : "Allow" ,
44+ "Principal" : {"Service" : "bedrock-agentcore.amazonaws.com" },
45+ "Action" : "sts:AssumeRole" ,
46+ },
47+ {
48+ "Effect" : "Allow" ,
49+ "Principal" : {"Service" : "lambda.amazonaws.com" },
50+ "Action" : "sts:AssumeRole" ,
51+ },
52+ ],
53+ }
54+
55+ _LAMBDA_INVOKE_POLICY_TEMPLATE = {
56+ "Version" : "2012-10-17" ,
57+ "Statement" : [
58+ {
59+ "Effect" : "Allow" ,
60+ "Action" : "lambda:InvokeFunction" ,
61+ "Resource" : None , # filled in after Lambda creation
62+ }
63+ ],
64+ }
65+
66+
67+ def _get_lambda_zip () -> bytes :
68+ """Package lambda_function.py into a zip archive."""
69+ lambda_path = os .path .join (os .path .dirname (__file__ ), "lambda_function" , "lambda_function.py" )
70+ buf = io .BytesIO ()
71+ with zipfile .ZipFile (buf , "w" , zipfile .ZIP_DEFLATED ) as zf :
72+ zf .write (lambda_path , "lambda_function.py" )
73+ return buf .getvalue ()
74+
75+
76+ def _ensure_role (iam_client ) -> str :
77+ """Create the gateway IAM role if it doesn't exist, return its ARN."""
78+ try :
79+ response = iam_client .get_role (RoleName = _ROLE_NAME )
80+ return response ["Role" ]["Arn" ]
81+ except iam_client .exceptions .NoSuchEntityException :
82+ pass
83+
84+ response = iam_client .create_role (
85+ RoleName = _ROLE_NAME ,
86+ AssumeRolePolicyDocument = json .dumps (_TRUST_POLICY ),
87+ Description = "Integration test role for AgentCore gateway tests" ,
88+ )
89+ role_arn = response ["Role" ]["Arn" ]
90+
91+ iam_client .attach_role_policy (
92+ RoleName = _ROLE_NAME ,
93+ PolicyArn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ,
94+ )
95+ # Wait for IAM propagation
96+ time .sleep (10 )
97+ logger .info ("Created role: %s" , role_arn )
98+ return role_arn
99+
100+
101+ def _attach_lambda_invoke_policy (iam_client , lambda_arn : str ):
102+ """Attach a scoped lambda:InvokeFunction policy to the gateway role."""
103+ policy = _LAMBDA_INVOKE_POLICY_TEMPLATE .copy ()
104+ policy ["Statement" ] = [{"Effect" : "Allow" , "Action" : "lambda:InvokeFunction" , "Resource" : lambda_arn }]
105+ iam_client .put_role_policy (
106+ RoleName = _ROLE_NAME ,
107+ PolicyName = "lambda-invoke" ,
108+ PolicyDocument = json .dumps (policy ),
109+ )
110+
111+
112+ def _ensure_lambda (lambda_client , role_arn : str ) -> str :
113+ """Create or update the test Lambda, return its ARN."""
114+ zip_bytes = _get_lambda_zip ()
115+ try :
116+ response = lambda_client .get_function (FunctionName = _LAMBDA_NAME )
117+ lambda_client .update_function_code (FunctionName = _LAMBDA_NAME , ZipFile = zip_bytes )
118+ return response ["Configuration" ]["FunctionArn" ]
119+ except lambda_client .exceptions .ResourceNotFoundException :
120+ pass
121+
122+ response = lambda_client .create_function (
123+ FunctionName = _LAMBDA_NAME ,
124+ Runtime = _LAMBDA_RUNTIME ,
125+ Role = role_arn ,
126+ Handler = _LAMBDA_HANDLER ,
127+ Code = {"ZipFile" : zip_bytes },
128+ Timeout = 30 ,
129+ Description = "MCP test Lambda for AgentCore gateway integration tests" ,
130+ )
131+ waiter = lambda_client .get_waiter ("function_active_v2" )
132+ waiter .wait (FunctionName = _LAMBDA_NAME )
133+ logger .info ("Created Lambda: %s" , response ["FunctionArn" ])
134+ return response ["FunctionArn" ]
135+
46136
47137class FixedIntentProvider (IntentProvider ):
48138 """Intent provider that returns a fixed string for deterministic testing."""
@@ -67,9 +157,18 @@ def setup_class(cls):
67157 cls .region = os .environ .get ("BEDROCK_TEST_REGION" , "us-west-2" )
68158 cls .role_arn = os .environ .get ("GATEWAY_ROLE_ARN" )
69159 cls .lambda_arn = os .environ .get ("GATEWAY_LAMBDA_ARN" )
160+ cls ._provisioned_infra = False
70161
71162 if not cls .role_arn or not cls .lambda_arn :
72- pytest .fail ("GATEWAY_ROLE_ARN and GATEWAY_LAMBDA_ARN must be set" )
163+ # Auto-provision infrastructure
164+ session = boto3 .Session (region_name = cls .region )
165+ iam_client = session .client ("iam" )
166+ lambda_client = session .client ("lambda" , region_name = cls .region )
167+ cls .role_arn = _ensure_role (iam_client )
168+ cls .lambda_arn = _ensure_lambda (lambda_client , cls .role_arn )
169+ _attach_lambda_invoke_policy (iam_client , cls .lambda_arn )
170+ cls ._provisioned_infra = True
171+ logger .info ("Auto-provisioned infrastructure: role=%s, lambda=%s" , cls .role_arn , cls .lambda_arn )
73172
74173 cls .gw_client = GatewayClient (region_name = cls .region )
75174 cls .test_prefix = f"sdk-integ-plugin-{ int (time .time ())} "
@@ -158,6 +257,33 @@ def teardown_class(cls):
158257 except Exception as e :
159258 logger .warning ("Failed to delete gateway %s: %s" , cls .gateway_id , e )
160259
260+ # Clean up auto-provisioned infrastructure
261+ if cls ._provisioned_infra :
262+ session = boto3 .Session (region_name = cls .region )
263+ lambda_client = session .client ("lambda" , region_name = cls .region )
264+ iam_client = session .client ("iam" )
265+ try :
266+ lambda_client .delete_function (FunctionName = _LAMBDA_NAME )
267+ logger .info ("Deleted Lambda: %s" , _LAMBDA_NAME )
268+ except Exception as e :
269+ logger .warning ("Failed to delete Lambda: %s" , e )
270+ try :
271+ iam_client .delete_role_policy (RoleName = _ROLE_NAME , PolicyName = "lambda-invoke" )
272+ except Exception :
273+ pass
274+ try :
275+ iam_client .detach_role_policy (
276+ RoleName = _ROLE_NAME ,
277+ PolicyArn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ,
278+ )
279+ except Exception :
280+ pass
281+ try :
282+ iam_client .delete_role (RoleName = _ROLE_NAME )
283+ logger .info ("Deleted role: %s" , _ROLE_NAME )
284+ except Exception as e :
285+ logger .warning ("Failed to delete role: %s" , e )
286+
161287 def _make_mcp_client (self ):
162288 """Create an MCPClient connected to the test gateway via Streamable HTTP with IAM auth."""
163289 from mcp_proxy_for_aws .client import aws_iam_streamablehttp_client
0 commit comments