Cross-service integration tests
fakecloud actually executes the wiring between services. Here's every supported integration.
The hardest bugs in AWS applications live in the wiring between services. A Lambda that's supposed to trigger on S3 uploads but doesn't. An EventBridge rule that fires but delivers to the wrong target. A Step Functions state machine that waits forever because the Lambda integration is shaped wrong.
Mocks can't catch those bugs. They test each service in isolation, and the wiring between them is exactly where the bugs hide.
fakecloud actually executes the cross-service wiring. When an EventBridge rule matches, it really delivers to the target. When an SES receipt rule evaluates, it really invokes the Lambda. When an S3 object is uploaded, it really publishes the notification. Tests that exercise end-to-end behavior work the same way they would against real AWS.
Supported integrations
Messaging and eventing:
- SNS -> SQS / Lambda / HTTP — Fan-out delivery to all subscription types.
- S3 -> SNS / SQS / Lambda / EventBridge — Bucket notifications on object create/delete.
- S3 -> Object Lambda Access Points —
GetObjectagainst an Object Lambda Access Point invokes the configured transforming Lambda. The Lambda'sWriteGetObjectResponsebody is actually persisted and returned to the caller (including status code, headers, andErrorCode/ErrorMessage), so the response your code sees matches what AWS would return. - EventBridge -> SNS / SQS / Lambda / Logs / Kinesis / Step Functions / HTTP — Rules deliver to targets on schedule or event match, including API Destinations.
- SQS -> Lambda — Event source mapping polls queues. Honors
FilterCriteria(drop non-matching),MaximumBatchingWindowInSeconds(hold partial batches), andFunctionResponseTypes=[ReportBatchItemFailures](Lambda response{"batchItemFailures":[{"itemIdentifier":...}]}retries only the failed messages). - Kinesis -> Lambda — Event source mapping polls shards. Honors
FilterCriteria(advances past dropped records) andStartingPosition(TRIM_HORIZON/LATEST/AT_TIMESTAMP) on first poll. - DynamoDB Streams -> Lambda — Event source mapping polls stream records. Honors
FilterCriteria(advances past dropped records) andStartingPosition(TRIM_HORIZON/LATEST). - DynamoDB -> Kinesis — Table changes stream to Kinesis Data Streams.
- CloudWatch Logs -> Lambda / Kinesis / SQS — Subscription filters deliver log events.
- CloudWatch Logs delivery configuration —
PutDeliverySource/PutDeliveryDestination/CreateDeliverypersist the source/destination/delivery tuples that AWS uses to wire services like API GW, AppSync, and Bedrock into Logs / Firehose / S3. The standarddelivery-destination-policytemplates (Logs, Firehose, S3) are accepted and returned exactly as AWS does, and the configuration survives acrossDescribe*calls so IaC tools see a stable shape. - Lambda async destinations -> SQS / SNS / EventBridge / Lambda —
InvocationType=Eventinvocations route their result throughOnSuccess/OnFailureusing AWS's standard destinations record schema. - Lambda -> CloudWatch Metrics — Every
Invoke(sync or async) publishes the standardAWS/Lambdanamespace metrics (Invocations,Errors,Throttles,Duration,ConcurrentExecutions) dimensioned byFunctionName.cloudwatch:GetMetricStatisticsandGetMetricDatareturn the recorded data points, so dashboards and alarm tests work without a separate metrics stub.
Identity and auth:
- Cognito -> Lambda — All 12 triggers: PreSignUp, PostConfirmation, PreAuthentication, PostAuthentication, CustomMessage, PreTokenGeneration, UserMigration, DefineAuthChallenge, CreateAuthChallenge, VerifyAuthChallengeResponse, CustomEmailSender, CustomSMSSender.
- Cognito -> SES — Verification emails for SignUp / ResendConfirmationCode / ForgotPassword / GetUserAttributeVerificationCode dispatch through SES (CustomEmailSender Lambda takes precedence when configured).
- Cognito -> SNS — SMS verification codes dispatch through SNS as
sms_messages(CustomSMSSender Lambda takes precedence when configured).
Email:
SES -> SNS / EventBridge / Kinesis / Firehose / CloudWatch Logs — Configuration set event destinations fan out send/delivery/bounce/complaint/open/click/reject/renderingFailure events to every destination type AWS supports. The same event payload AWS would emit is delivered to each destination —
PutRecordfor Kinesis,PutRecordBatchfor Firehose,PutLogEventsfor CloudWatch Logs.SES Inbound -> S3 / SNS / Lambda — Receipt rules evaluate inbound email and execute S3, SNS, and Lambda actions for real.
SES -> SMTP relay (outbound) — Set
FAKECLOUD_SES_SMTP_RELAY=smtp://user:pass@host:portandSendEmail/SendRawEmail/SendBulkEmailactually deliver to the configured SMTP server in addition to recording the message. Useful for end-to-end tests against MailHog, Mailpit, or a real mail server. Unset (the default) keeps SES purely in-memory.FAKECLOUD_SES_SMTP_RELAY=smtp://mailhog:1025 fakecloud
Orchestration and APIs:
- Step Functions -> Lambda / SQS / SNS / EventBridge / DynamoDB — Task states invoke Lambda, send SQS messages, publish to SNS topics, put EventBridge events, and read/write DynamoDB items.
- Step Functions -> Step Functions (nested executions) — Task states with resource
arn:aws:states:::states:startExecution(and the.syncvariant) start a child state machine execution. The.syncform waits for the child to terminate and returns its output; the fire-and-forget form returns immediately with the child's execution ARN. - API Gateway v2 -> Lambda — HTTP API routes invoke Lambda functions with proxy integration v2.0 format.
- API Gateway v2 -> CloudWatch Logs (access logs) — Stages with
AccessLogSettings.DestinationArnset to a Logs log group publish onePutLogEventsentry per request, formatted per the stage'sFormattemplate (defaulting to the standard$contextplaceholders).
Infrastructure:
- CloudFormation -> Lambda / SNS — Custom resources invoke via
ServiceToken, stack events notify viaNotificationARNs. - CloudFormation nested stacks + SAM transform —
AWS::CloudFormation::Stackresources fetch the child template (TemplateURLfrom S3 or an inline body) and provision its resources in a child stack, propagating outputs back viaFn::GetAtt.Transform: AWS::Serverless-2016-10-31expandsAWS::Serverless::Function/Api/SimpleTableinto native Lambda / API GW v2 / DynamoDB resources before provisioning. - CloudFormation provisioners (Firehose, Glue, Athena, Application Auto Scaling, SES, WAFv2) — Stack templates create and update real resources for
AWS::KinesisFirehose::DeliveryStream,AWS::Glue::Database/Table/Crawler/Job,AWS::Athena::WorkGroup/NamedQuery,AWS::ApplicationAutoScaling::ScalableTarget/ScalingPolicy,AWS::SES::ConfigurationSet/EventDestination/Template, andAWS::WAFv2::WebACL/IPSet/RuleGroup.Fn::GetAtton SESConfigurationSet.Arnand WAFv2WebACL.Arn/Id/Capacityreturns the live values. - Secrets Manager -> Lambda — Rotation invokes Lambda for all 4 steps.
- Secrets Manager -> KMS — When a secret has
KmsKeyId,CreateSecret/PutSecretValuecallkms:GenerateDataKeyandGetSecretValuecallskms:Decryptwith the AWS-shaped encryption context{aws:secretsmanager:secretArn: <arn>}. Auto-provisions theaws/secretsmanagerAWS-managed key on first use. All KMS calls are recorded at/_fakecloud/kms/usage. - SSM SecureString -> KMS —
PutParameterwithType=SecureStringcallskms:GenerateDataKey;GetParameter*withWithDecryption=truecallskms:Decrypt. The encryption context is{PARAMETER_ARN: <arn>}and the defaultaws/ssmkey auto-provisions on first use; passKeyIdfor a customer-managed key. - S3 SSE-KMS -> KMS —
PutObjectwithServerSideEncryption=aws:kmscallskms:GenerateDataKey;GetObjectdecrypts viakms:Decrypt. The encryption context is{aws:s3:arn: arn:aws:s3:::<bucket>}and ranged reads are sliced from plaintext, not the stored ciphertext envelope. The defaultaws/s3key auto-provisions on first use. - SQS encrypted queue -> KMS —
SendMessageon a queue withKmsMasterKeyIdcallskms:GenerateDataKey;ReceiveMessagecallskms:Decryptand returns the plaintext body. Encryption context:{aws:sqs:arn: <queue-arn>}. The on-queue body is the ciphertext envelope; only the returned copy is decrypted. - SNS encrypted topic -> KMS —
Publishto a topic withKmsMasterKeyIdrecords the matchingkms:GenerateDataKeyandkms:Decryptaudit-trail records (SNS encrypts at rest then decrypts to fan-out). Encryption context:{aws:sns:arn: <topic-arn>}. - SNS -> SQS encrypted queue (SSE fan-out) — SNS topic fan-out into an SSE-enabled SQS queue stores the ciphertext envelope on the queue and records
kms:GenerateDataKeyagainst the queue's CMK.ReceiveMessagedecrypts back to the original SNS notification body. The KMS audit trail at/_fakecloud/kms/usagereflects both the SNS-side and SQS-side calls. - SNS -> SMTP relay (email subscriptions) — Email and email-json subscriptions deliver via the same
FAKECLOUD_SES_SMTP_RELAYrelay that SES uses. Without the env var, email deliveries are recorded in memory; with it set, the relay actually sends a MIME message. - DynamoDB encrypted table -> KMS —
PutItem/UpdateItemon a table withSSESpecification.SSEType=KMSrecordskms:GenerateDataKey;GetItem/Query/Scanrecordskms:Decrypt. Item bodies are not actually encrypted — fakecloud emits the audit-trail records the AWS API would produce so callers can assert KMS usage on encrypted tables. - S3 Lifecycle — Background expiration and storage class transitions.
- EventBridge Scheduler — Cron and rate-based rules fire on schedule.
- RDS Postgres -> Lambda (
aws_lambdaextension) — The prebuiltghcr.io/faiscadev/fakecloud-postgresimage ships theaws_lambdaextension.SELECT aws_lambda.invoke('my-fn', ...)reaches the fakecloud Lambda control plane and returns the function's payload, so tests for Postgres triggers that fan out to Lambda run end-to-end. - RDS Postgres -> S3 (
aws_s3extension) —aws_s3.query_export_to_s3andaws_s3.table_import_from_s3move CSV between Postgres and fakecloud S3 buckets via the same image. - RDS MySQL / MariaDB -> Lambda (Aurora
lambda_async) — The MySQL and MariaDB images exposemysql.lambda_async('arn:...', '{"payload":1}')and fire-and-forget invoke against fakecloud Lambda. - RDS -> EventBridge — DB instance and snapshot lifecycle ops (create, modify, delete, reboot, start, stop, snapshot create/delete, restore) emit
aws.rdsevents that match the AWS event schema. - ECS -> EventBridge — Task state transitions emit
ECS Task State Changeevents on the default bus. Eventdetailcarries the task ARN, cluster ARN, last status, stop code/reason on STOPPED, and a per-container summary including exit code. - ECS -> ELBv2 — A service registered with
loadBalancers[]callselbv2:RegisterTargetsagainst the named target group every time a task reachesRUNNING, andDeregisterTargetswhen the task stops or the service scales down. The target group'sTargetslist reflects the live task IPs, soDescribeTargetHealthreturns what the data plane would. - ECS -> ECR — Task definitions that reference AWS-private-ECR URIs (
<account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>) resolve against fakecloud's local OCI v2 endpoint at runtime. The runtime pulls from127.0.0.1:<port>/<repo>:<tag>, retags to the AWS URI, and runs the container under the user-visible image name. Same resolution applies to Lambda functions deployed withPackageType=Imageand a fakecloud ECRCode.ImageUri. - ECS awslogs -> CloudWatch Logs — Containers declaring
logDriver=awslogsget every captured stdout/stderr line forwarded to fakecloud Logs. The runtime honorsawslogs-create-group=true, creates a stream named<prefix>/<container-name>/<task-id>, and downstream subscription filters fire on the appended events. - ECS task secrets -> Secrets Manager / SSM — Container
secrets[]entries with a SecretsManager or SSM Parameter Store ARN are resolved synchronously at task launch and injected as environment variables beforedocker run. Missing values fail the task withstopCode=TaskFailedToStart, mirroring real ECS. - ECS task role -> IAM credentials — Tasks registered with a
taskRoleArngetAWS_CONTAINER_CREDENTIALS_FULL_URIinjected into every container, pointing at a fakecloud-local IMDS-format credential endpoint. AWS SDKs pick this up via the default credential-provider chain soaws sts get-caller-identity(and any other SDK call) works from inside the container. - ECR signing -> KMS — When a repository has
cosignkeyed-mode (ECDSA-P256) verification configured,PutImagerequires a matching signature manifest in the registry. Unsigned or wrong-key images are rejected. - IAM PassRole trust enforcement — Lambda
CreateFunctionand ECSRegisterTaskDefinition/RunTaskoverrides reject role ARNs whoseAssumeRolePolicyDocumentdoesn't list the calling service principal (lambda.amazonaws.com,ecs-tasks.amazonaws.com), the same way real AWS does.
Testing a cross-service flow
The pattern is the same for all of them: configure the wiring via the normal AWS SDK, trigger the upstream event, then assert on the downstream state via the fakecloud SDK.
Example — S3 upload triggers Lambda via an event source:
import { FakeCloud } from "fakecloud";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const fc = new FakeCloud();
// (assume bucket, Lambda, and notification config already created via SDK)
await new S3Client({ endpoint: "http://localhost:4566", /* ... */ }).send(
new PutObjectCommand({ Bucket: "my-bucket", Key: "uploads/hello.txt", Body: "hi" })
);
// Lambda really ran. Assert on its invocations.
const { invocations } = await fc.lambda.getInvocations();
expect(invocations).toHaveLength(1);
expect(invocations[0].event.Records[0].s3.object.key).toBe("uploads/hello.txt");No mocks. The S3 upload actually triggered the notification, which actually invoked the Lambda, which actually ran and recorded its invocation — all in the same fakecloud process. If any step of that wiring is broken in your code, the test fails the same way it would against real AWS.
What doesn't exist yet
If you need a cross-service integration that isn't in the list above, open an issue. The ones that exist were driven by real user needs, and the list keeps growing.