Skip to content
AWS docs1.0

Testing and Operating AWS Integrations

Latest stable Based on AWS release 1.0.0

Test the AWS boundary that can fail in production: request mapping, endpoint and credentials selection, retry and acknowledgement behavior, client ownership, and shutdown. A mocked client is useful for pure delegation logic, but it does not prove transport, serialization, emulator compatibility, or lifecycle.

Floci-first emulator test flow

LevelWhat it provesWhat it does not prove
Unit or mock testRequest mapping, branching, conversion, retry classificationReal SDK transport, endpoint behavior, IAM, or lifecycle
Floci integration testFast default path for supported AWS APIs through TestcontainersOperations not implemented by Floci or AWS production behavior
LocalStack fallbackAn operation or cross-service path supported by LocalStackAWS IAM, quota, latency, managed-service policy, or full semantic parity
AWS environment smoke testCurrent credentials, region, endpoint, and selected service can work togetherRepeatable failure recovery, scale, or broad policy coverage
Load or resilience testOne declared workload and failure modelA universal throughput or cost claim

Repository tests default bluetape4k.aws.emulator to floci. Keep that default for fast supported paths. When Floci lacks an operation or a cross-service integration needs LocalStack, select it explicitly:

Terminal window
./gradlew :bluetape4k-aws-java:test \
-Dbluetape4k.aws.emulator=localstack
./gradlew :aws-spring-boot-sqs-examples:test \
-Dbluetape4k.aws.emulator=localstack

Do not hide emulator gaps by skipping assertions. Mark unsupported operations clearly and keep mock or fallback coverage tied to the gap. Passing against either emulator does not prove production IAM or service limits.

The Issue #313 Step Functions helpers are an unreleased/develop surface. Run their dedicated smoke checks through the Floci-first selector before trying the LocalStack fallback:

Terminal window
./gradlew :bluetape4k-aws-java:test \
--tests "*Sfn*SmokeTest" \
-Dbluetape4k.aws.emulator=floci \
--no-daemon --max-workers=1 --console=plain
./gradlew :bluetape4k-aws-kotlin:test \
--tests "*Sfn*SmokeTest" \
-Dbluetape4k.aws.emulator=floci \
--no-daemon --max-workers=1 --console=plain

The current Floci guard records the exact message live integration unverified: Floci does not support Step Functions and keeps the skipped test result. This is UNVERIFIED, not a live-integration pass. For a coverage gap, run the dedicated fixtures sequentially with LocalStack:

Terminal window
./gradlew :bluetape4k-aws-java:test \
--tests "*Sfn*SmokeTest" \
-Dbluetape4k.aws.emulator=localstack \
--no-daemon --max-workers=1 --console=plain
./gradlew :bluetape4k-aws-kotlin:test \
--tests "*Sfn*SmokeTest" \
-Dbluetape4k.aws.emulator=localstack \
--no-daemon --max-workers=1 --console=plain

If LocalStack does not support the operation or the smoke fails, preserve the test result XML and record live integration unverified: LocalStack Step Functions smoke failed: <error>. Do not change the result to PASS. Emulator success proves endpoint and basic serialization compatibility only; it does not prove AWS IAM resource policies, KMS key policies, service quotas, latency, or managed-service behavior. Keep IAM/KMS integration UNVERIFIED until a separate credential-gated AWS check exists.

For production polling, enforce a caller-owned timeout or deadline, keep each collector at or above the helper’s one-second interval, and limit aggregate account/Region polling below the current Step Functions service quotas. Use shareIn or stateIn for multiple observers of one execution, and add caller-owned jitter or rate limiting when collectors start together. Record service, operation, status, outcome, latency, retry/throttle counts, and the AWS request ID; redact ARNs, names, input, output, errors, causes, trace headers, and raw SDK payloads. Cancellation must stop collection without an implicit StopExecution; close the service client at the owner boundary and leave an injected HTTP client or engine under caller ownership.

Terminal window
./gradlew :aws-ktor-s3-examples:test
./gradlew :aws-ktor-dynamodb-examples:test
./gradlew :aws-ktor-sqs-examples:test
./gradlew :aws-ktor-exposed-examples:test
./gradlew :aws-spring-boot-s3-examples:test
./gradlew :aws-spring-boot-dynamodb-examples:test
./gradlew :aws-spring-boot-sqs-examples:test
./gradlew :aws-spring-boot-exposed-examples:test

For service code, add failure cases: duplicate SQS delivery, visibility timeout expiry, conditional DynamoDB write failure, partial S3 transfer, expired presigned URL, unavailable secret source, and shutdown while work is in flight.

  • Credentials and region — use the provider chain appropriate to the deployment. Never log access keys, session tokens, secret payloads, or signed headers.
  • Retries and idempotency — retry only operations whose business effect is safe. Carry idempotency keys into consumers and writes where duplicates are possible.
  • Timeouts — configure connect, request, polling, handler, and shutdown timeouts against the service-level budget.
  • Readiness — report ready only after required clients, queues, tables, buckets, or database pools can serve the path the application requires.
  • Observability — record service, operation, outcome, retry count, latency, queue phase, and low-cardinality resource identifiers. Avoid bucket keys, message bodies, and secret names when they can expose user data.
  • Shutdown — stop polling, drain or cancel in-flight work, flush bounded buffers, close clients and pools, then close any explicitly shared HTTP engine.

Add a lifecycle test whenever a component can either create or receive a client. Verify that owned clients close exactly once and injected clients remain open. For shared Kotlin SDK engines, verify the engine stays available while another client uses it and is closed by the application only after every client stops.