From 26e6ee0a647efad8fbe9437650e1bd0391445915 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 28 Feb 2025 17:21:56 +0000 Subject: [PATCH 1/7] Initial commit --- README.md | 16 ++ .../apigateway-crud-lambda-dynamodb/README.md | 159 ++++++++++++++ .../events/lambda-create-event.json | 3 + .../events/lambda-delete-event.json | 3 + .../events/lambda-init-event.json | 5 + .../events/lambda-read-event.json | 3 + .../events/lambda-update-event.json | 3 + .../img/apigateway-crud-lambda-dynamodb.png | Bin 0 -> 70913 bytes .../lambda_crud_create_src/app.py | 22 ++ .../lambda_crud_delete_src/app.py | 27 +++ .../lambda_crud_init_src/app.py | 52 +++++ .../lambda_crud_read_src/app.py | 46 +++++ .../lambda_crud_update_src/app.py | 55 +++++ .../template.yaml | 131 ++++++++++++ local-test-samples/apigateway-mock/README.md | 116 +++++++++++ .../apigateway-mock/img/apigateway-mock.png | Bin 0 -> 59925 bytes .../apigateway-mock/lambda_mock_src/app.js | 7 + .../apigateway-mock/template.yaml | 25 +++ .../dynamodb-crud-cli/README.md | 132 ++++++++++++ .../dynamodb-crud-cli/create-item.json | 4 + .../dynamodb-crud-cli/delete-item.json | 3 + .../img/dynamodb-crud-cli.png | Bin 0 -> 56353 bytes .../dynamodb-crud-cli/update-item.json | 4 + .../dynamodb-crud-lambda/README.md | 128 ++++++++++++ .../events/lambda-create-event.json | 3 + .../events/lambda-delete-event.json | 3 + .../events/lambda-init-event.json | 5 + .../events/lambda-read-event.json | 3 + .../events/lambda-update-event.json | 3 + .../img/dynamodb-crud-lambda.png | Bin 0 -> 49967 bytes .../lambda_crud_create_src/app.py | 23 +++ .../lambda_crud_delete_src/app.py | 24 +++ .../lambda_crud_init_src/app.py | 48 +++++ .../lambda_crud_read_src/app.py | 33 +++ .../lambda_crud_update_src/app.py | 47 +++++ .../dynamodb-crud-lambda/template.yaml | 89 ++++++++ .../dynamodb-crud-stepfunctions/README.md | 143 +++++++++++++ .../aws-stepfunctions-local-credentials.txt | 4 + .../img/dynamodb-crud-stepfunctions.png | Bin 0 -> 47727 bytes .../state-machine.json | 136 ++++++++++++ .../lambda-sam-helloworld/README.md | 112 ++++++++++ .../events/lambda-helloworld-event.json | 5 + .../img/lambda-sam-helloworld.png | Bin 0 -> 38055 bytes .../lambda_helloworld_src/app.py | 7 + .../lambda-sam-helloworld/template.yaml | 18 ++ .../lambda-sam-layers/README.md | 120 +++++++++++ .../custom-lambda-layer/requirements.txt | 1 + .../events/lambda-layers-event.json | 5 + .../img/lambda-sam-layers.png | Bin 0 -> 68083 bytes .../lambda_layers_src/app.py | 9 + .../lambda-sam-layers/template.yaml | 32 +++ .../stepfunctions-helloworld/README.md | 150 ++++++++++++++ .../aws-stepfunctions-local-credentials.txt | 3 + .../img/stepfunctions-helloworld.png | Bin 0 -> 53015 bytes .../stepfunctions-lambda/README.md | 195 ++++++++++++++++++ .../aws-stepfunctions-local-credentials.txt | 4 + .../img/stepfunctions-lambda.png | Bin 0 -> 56881 bytes .../lambda_stepfunctions_square_src/app.js | 11 + .../lambda_stepfunctions_sum_src/app.js | 15 ++ .../stepfunctions-lambda/template.yaml | 71 +++++++ .../stepfunctions-mock/MockConfigFile.json | 71 +++++++ .../stepfunctions-mock/README.md | 172 +++++++++++++++ .../aws-stepfunctions-local-credentials.txt | 3 + .../img/stepfunctions-mock.png | Bin 0 -> 47237 bytes .../stepfunctions-mock/statemachine.json | 34 +++ 65 files changed, 2546 insertions(+) mode change 100644 => 100755 README.md create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/README.md create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_create_src/app.py create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_delete_src/app.py create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_init_src/app.py create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_read_src/app.py create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_update_src/app.py create mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/template.yaml create mode 100755 local-test-samples/apigateway-mock/README.md create mode 100755 local-test-samples/apigateway-mock/img/apigateway-mock.png create mode 100755 local-test-samples/apigateway-mock/lambda_mock_src/app.js create mode 100755 local-test-samples/apigateway-mock/template.yaml create mode 100755 local-test-samples/dynamodb-crud-cli/README.md create mode 100755 local-test-samples/dynamodb-crud-cli/create-item.json create mode 100755 local-test-samples/dynamodb-crud-cli/delete-item.json create mode 100755 local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png create mode 100755 local-test-samples/dynamodb-crud-cli/update-item.json create mode 100755 local-test-samples/dynamodb-crud-lambda/README.md create mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json create mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json create mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json create mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json create mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json create mode 100755 local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png create mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_create_src/app.py create mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_delete_src/app.py create mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_init_src/app.py create mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_read_src/app.py create mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_update_src/app.py create mode 100755 local-test-samples/dynamodb-crud-lambda/template.yaml create mode 100755 local-test-samples/dynamodb-crud-stepfunctions/README.md create mode 100755 local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt create mode 100755 local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png create mode 100755 local-test-samples/dynamodb-crud-stepfunctions/state-machine.json create mode 100755 local-test-samples/lambda-sam-helloworld/README.md create mode 100755 local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json create mode 100755 local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png create mode 100755 local-test-samples/lambda-sam-helloworld/lambda_helloworld_src/app.py create mode 100755 local-test-samples/lambda-sam-helloworld/template.yaml create mode 100755 local-test-samples/lambda-sam-layers/README.md create mode 100755 local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt create mode 100755 local-test-samples/lambda-sam-layers/events/lambda-layers-event.json create mode 100755 local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png create mode 100755 local-test-samples/lambda-sam-layers/lambda_layers_src/app.py create mode 100755 local-test-samples/lambda-sam-layers/template.yaml create mode 100755 local-test-samples/stepfunctions-helloworld/README.md create mode 100755 local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt create mode 100755 local-test-samples/stepfunctions-helloworld/img/stepfunctions-helloworld.png create mode 100755 local-test-samples/stepfunctions-lambda/README.md create mode 100755 local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt create mode 100755 local-test-samples/stepfunctions-lambda/img/stepfunctions-lambda.png create mode 100755 local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js create mode 100755 local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js create mode 100755 local-test-samples/stepfunctions-lambda/template.yaml create mode 100755 local-test-samples/stepfunctions-mock/MockConfigFile.json create mode 100755 local-test-samples/stepfunctions-mock/README.md create mode 100755 local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt create mode 100755 local-test-samples/stepfunctions-mock/img/stepfunctions-mock.png create mode 100755 local-test-samples/stepfunctions-mock/statemachine.json diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 36e0af2a..1cd775f6 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ If you'd like to jump right into code, you can begin with a starter project in y - [Java starter](./java-test-samples/apigw-lambda-list-s3-buckets) - [TypeScript starter](./typescript-test-samples/typescript-test-intro) - [.NET starter](./dotnet-test-samples/apigw-lambda-list-s3-buckets) +- [Local starter](./local-test-samples/apigateway-crud-lambda-dynamodb) # Language Directories The repository is divided into several language directories. If you would like to browse by language, you can navigate to the main page of each language directory: @@ -30,6 +31,8 @@ The repository is divided into several language directories. If you would like t - [Java main directory](./java-test-samples/) - [TypeScript main directory](./typescript-test-samples/) - [.NET main directory](./dotnet-test-samples/) +- [Local starter](./local-test-samples/) + # Workload Types This repository contains sample code for testing a variety of different types of workloads, including API's, Event-Driven Architectures, Service Orchestration, Data Processing, and AWS Partner Patterns. @@ -45,6 +48,8 @@ This repository contains sample code for testing a variety of different types of | [API Gateway, Lambda Authorizer, Lambda, DynamoDB](https://github.com/aws-samples/serverless-samples/tree/main/serverless-rest-api/javascript-http-sam) [External] | Node.js | | [API Gateway, Lambda, S3](./dotnet-test-samples/apigw-lambda-list-s3-buckets)|.NET| | [API Gateway, Lambda, DynamoDB](./dotnet-test-samples/apigw-lambda-ddb)|.NET| +| [Api Gateway, Lambda, DynamoDB](./local-test-samples/apigateway-crud-lambda-dynamodb)|Local| +| [Api Gateway, MOCK](./local-test-samples/apigateway-mock)|Local| | [SQS, Lambda, DynamoDB](./dotnet-test-samples/sqs-lambda)|.NET| | [API Gateway, Lambda, DynamoDB](./java-test-samples/apigw-lambda-ddb)|Java| | [AppSync, DynamoDB](./java-test-samples/java-appsync-sam)|Java| @@ -57,6 +62,8 @@ Event-driven architectures (EDA) are an architecture style that uses events and |[Schemas and Contracts](./typescript-test-samples/schema-and-contract-testing)|TypeScript|Event driven architectures decouple producers and consumers at the infrastructure layer, but these resources may still be coupled at the application layer by the event contract. Learn how to test for breaking changes in the contract.| |[S3, Lambda, DynamoDB](./dotnet-test-samples/async-lambda-dynamodb)|.NET|This example shows how to test async system by using DynamoDB during to store incoming asynchronous events during testing| |[S3, Lambda, SQS](./dotnet-test-samples/async-lambda-sqs)|.NET|An example to how to test asynchronous workflow by long polling the queue that resulting messages are sent to.| +|[Lambda, SAM, HelloWorld](./local-test-samples/lambda-sam-helloworld)|Local| Local Lambda HelloWorld implementation | +|[Lambda, SAM, Layers](./local-test-samples/lambda-sam-layers)|Local| Local Lambda emulator Layers implementation | ## Architectural patterns |Pattern|Services used|Language|Description| @@ -67,6 +74,9 @@ Event-driven architectures (EDA) are an architecture style that uses events and |System Under Test|Language|Description| |---|---|---| | [Step Functions](./java-test-samples/step-functions-local) [External] |Java|This project shows a technique for testing an AWS Step Functions workflow in a local desktop environment.| +| [Step Functions, HelloWorld](./local-test-samples/stepfunctions-helloworld) |Local| Local Hello World Step Functions implementation | +| [Step Functions, Lambda](./local-test-samples/stepfunctions-lambda) |Local| Local Lambda execution integrated with local Hello World Step Functions implementation | +| [Step Functions, MOCK](./local-test-samples/stepfunctions-mock) |Local| Mocking answers from local Hello World Step Functions implementation | ## Data Processing | System Under Test|Language|Description| @@ -74,6 +84,12 @@ Event-driven architectures (EDA) are an architecture style that uses events and |[Kinesis Data Stream, Lambda](./typescript-test-samples/kinesis-lambda-dynamodb)|TypeScript|This project shows a technique for testing a streaming data processing system.| |[Kinesis Data Stream, Lambda, DynamoDB](./dotnet-test-samples/kinesis-lambda-dynamodb)|.NET|This pattern creates an AWS Lambda function that consumes messages from an Amazon Kinesis Data Streams and dumps them to Amazon DynamoDB using SAM and .NET 6.| +## Database +| System Under Test|Language|Description| +| [DynamoDB, aws cli](./local-test-samples/dynamodb-crud-cli)|Local| DynamoDB CRUD operations using local docker image and aws cli | +| [DynamoDB, Lambda](./local-test-samples/dynamodb-crud-lambda)|Local| DynamoDB CRUD operations using docker image and local lambda local emulator | +| [DynamoDB, StepFunctions](./local-test-samples/dynamodb-crud-stepfunctions)|Local| DynamoDB CRUD operations using docker image and step functions local emulator | + ## AWS Partner Patterns | Partner |System Under Test|Language|Description| |---|---|---|---| diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/README.md b/local-test-samples/apigateway-crud-lambda-dynamodb/README.md new file mode 100755 index 00000000..9f7e9a77 --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/README.md @@ -0,0 +1,159 @@ +[![python: 3.9](https://img.shields.io/badge/Python-3.9-green)](https://img.shields.io/badge/Python-3.9-green) +[![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) +[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-orange)](https://img.shields.io/badge/AWS-Lambda-orange) +[![AWS: API Gateway](https://img.shields.io/badge/AWS-API%20Gateway-blue)](https://img.shields.io/badge/AWS-API%20Gateway-blue) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# Local: Amazon Api Gateway, AWS Lambda, Amazon DynamoDB CRUD Operations + +## Introduction +This project demonstrates how to perform local testing of AWS serverless applications using SAM CLI and Docker. It implements a complete CRUD operation cycle through API Gateway, with Lambda functions processing the requests and DynamoDB storing the data. + +--- + +## Contents +- [Local: Amazon Api Gateway, AWS Lambda, Amazon DynamoDB CRUD Operations](#local-amazon-api-gateway-aws-lambda-amazon-dynamodb-crud-operations) + - [Introduction](#introduction) + - [Contents](#contents) + - [Project Structure](#project-structure) + - [Architecture Overview](#architecture-overview) + - [Local Setup Requirements](#local-setup-requirements) + - [Running the Tests](#running-the-tests) + - [API Endpoints](#api-endpoints) + - [Test Sequence](#test-sequence) + - [Additional Resources](#additional-resources) + +--- + +## Project Structure +``` +├── apigateway-crud-lambda-dynamodb _# folder containing necessary code and template for CRUD operations with API Gateway, Lambda Functions and DynamoDB_ +│ ├── events _# folder containing json files for API Gateway CRUD input events_ +│ ├── img/apigateway-crud-lambda-dynamodb.png _# Architecture diagram_ +│ ├── lambda_crud_src _# folder containing code for different CRUD Lambda functions_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ + +``` + +[Top](#contents) + +--- + +## Architecture Overview + +

+ API Gateway - CRUD + Lambda + DynamoDB +

+ +The application consists of: +- API Gateway (emulated locally by SAM) +- Lambda functions (Python 3.9) (emulated locally by SAM) +- DynamoDB table (running locally via Docker) + +[Top](#contents) + +--- + +## Local Setup Requirements +- Docker (SAM emulation) +- AWS SAM CLI +- Python 3.9 +- curl (for testing) + +[Top](#contents) + +--- + +## Running the Tests + +1. Start DynamoDB locally: +```sh +docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local +``` + +2. Start the API Gateway emulator: +```sh +sam local start-api --docker-network host & +``` + +3. Initialize the DynamoDB table (though Api Gateway -> Lambda crud init function): +```sh +curl -X GET http://127.0.0.1:3000/init +``` + +[Top](#contents) + +--- + +## API Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| /init | GET | Creates DynamoDB CRUDLocalTable table | +| /create | POST | Creates new item | +| /read | GET | Retrieves an item | +| /update | POST | Updates existing item | +| /delete | GET | Deletes an item | + +[Top](#contents) + +--- + +## Test Sequence + +1. Create initial item: +```sh +curl -X POST http://127.0.0.1:3000/create \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123", "name": "Batman"}' +``` + +2. Read item: +```sh +curl -X GET http://127.0.0.1:3000/read \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123"}' +``` + +3. Update initial item: +```sh +curl -X POST http://127.0.0.1:3000/update \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123", "name": "Robin"}' +``` + +4. Check updated item: +```sh +curl -X GET http://127.0.0.1:3000/read \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123"}' +``` + +5. Delete item: +```sh +curl -X GET http://127.0.0.1:3000/delete \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123"}' +``` + +6. Checking item does not exist: +```sh +curl -X GET http://127.0.0.1:3000/read \ + -H 'Content-Type: application/json' \ + -d '{"Id": "123"}' +``` + +[Top](#contents) + +--- + +## Additional Resources +- [API Gateway Docs SAM] [AWS Serverless Application Model - Developer Guide - Locally run API Gateway with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-start-api.html) +- [Lambda Docs SAM] [AWS Serverless Application Model - Developer Guide - Locally invoke Lambda functions with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) +- [Amazon DynamoDB Developer Guide] [Amazon DynamoDB - Developer Guide - Deploying DynamoDB locally on your computer](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html) + + +[Top](#contents) + +--- \ No newline at end of file diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json new file mode 100755 index 00000000..1c47303f --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json @@ -0,0 +1,3 @@ +{ + "body": "{\"Id\": \"123\", \"name\": \"Batman\"}" +} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json new file mode 100755 index 00000000..12090c66 --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json @@ -0,0 +1,3 @@ +{ + "Id": "123" +} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json new file mode 100755 index 00000000..4ccb6ff4 --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json @@ -0,0 +1,5 @@ +{ + "key1": "value1", + "key2": "value2", + "key3": "value3" +} \ No newline at end of file diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json new file mode 100755 index 00000000..12090c66 --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json @@ -0,0 +1,3 @@ +{ + "Id": "123" +} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json new file mode 100755 index 00000000..074569d3 --- /dev/null +++ b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json @@ -0,0 +1,3 @@ +{ + "body": "{\"Id\": \"123\", \"name\": \"Robin\"}" +} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png b/local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png new file mode 100755 index 0000000000000000000000000000000000000000..91cd84de657d7f3295e95f7987ca7563c2dd7368 GIT binary patch literal 70913 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

jAGYB{O<2L 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 + API Gateway - MOCK +

+ +Components: +- API Gateway emulator (local) +- Lambda function returning mock response +- No external service dependencies + +--- + +## Local Testing Steps + +1. Navigate to project directory: +```sh +cd apigateway-mock +``` + +2. Start API Gateway emulator: +```sh +sam local start-api & +``` + +Expected output: +``` +Mounting LambdaMockFunction at http://127.0.0.1:3000/MOCK [GET] +Running on http://127.0.0.1:3000 +``` + +3. Test the endpoint: +```sh +curl http://127.0.0.1:3000/MOCK +``` + +Expected response: +```json +"This is mock response" +``` + +--- + +## API Documentation + +| Endpoint | Method | Response Type | Description | +|----------|---------|---------------|-------------| +| /MOCK | GET | JSON | Returns a mock response string | + +**Sample Response:** +```json +"This is mock response" +``` + +--- + +## Additional Resources +- [AWS SAM API Gateway Local Testing Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-start-api.html) +- [SAM Local API Testing Introduction](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-start-api.html) + +[Top](#contents) + +--- \ No newline at end of file diff --git a/local-test-samples/apigateway-mock/img/apigateway-mock.png b/local-test-samples/apigateway-mock/img/apigateway-mock.png new file mode 100755 index 0000000000000000000000000000000000000000..d88090d41f8dbe0c167cbe66f8929d31d87fac3f GIT binary patch literal 59925 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 X{UR@}*KhM|7=Xaj)z4*}Q$iB})pk)x literal 0 HcmV?d00001 diff --git a/local-test-samples/apigateway-mock/lambda_mock_src/app.js b/local-test-samples/apigateway-mock/lambda_mock_src/app.js new file mode 100755 index 00000000..3d88f415 --- /dev/null +++ b/local-test-samples/apigateway-mock/lambda_mock_src/app.js @@ -0,0 +1,7 @@ +exports.lambdaHandler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('This is mock response'), + }; + return response; +}; diff --git a/local-test-samples/apigateway-mock/template.yaml b/local-test-samples/apigateway-mock/template.yaml new file mode 100755 index 00000000..fd5be20c --- /dev/null +++ b/local-test-samples/apigateway-mock/template.yaml @@ -0,0 +1,25 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: AWS SAM Template for API Gateway with Mock and Lambda integration + +Resources: + # API Gateway with MOCK integration + APIGatewayMock: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + + # Lambda function to run the MOCK + LambdaMockFunction: + Type: AWS::Serverless::Function + Properties: + Handler: app.lambdaHandler + Runtime: nodejs18.x + CodeUri: lambda_mock_src/ + Events: + EventMock: + Type: Api + Properties: + Path: /MOCK + Method: get + RestApiId: !Ref APIGatewayMock diff --git a/local-test-samples/dynamodb-crud-cli/README.md b/local-test-samples/dynamodb-crud-cli/README.md new file mode 100755 index 00000000..3cdd52d8 --- /dev/null +++ b/local-test-samples/dynamodb-crud-cli/README.md @@ -0,0 +1,132 @@ +[![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) +[![AWS: CLI](https://img.shields.io/badge/AWS-CLI-yellow)](https://img.shields.io/badge/AWS-CLI-yellow) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# Local: Amazon DynamoDB CRUD Operations with AWS CLI + +## Introduction +This project demonstrates how to perform local testing of Amazon DynamoDB using Docker and AWS CLI. It provides examples of complete CRUD (Create, Read, Update, Delete) operations without requiring actual AWS infrastructure. + +--- + +## Contents +- [Local: Amazon DynamoDB CRUD Operations with AWS CLI](#local-amazon-dynamodb-crud-operations-with-aws-cli) + - [Introduction](#introduction) + - [Contents](#contents) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Architecture Overview](#architecture-overview) + - [Setup Steps](#setup-steps) + - [CRUD Operations](#crud-operations) + - [Additional Resources](#additional-resources) + +--- + +## Project Structure +``` +├── dynamodb-crud-cli _# folder containing json files for aws cli DynamoDB CRUD operations_ +│ ├── create-item.json _# json file containing create item aws cli input_ +│ ├── delete-item.json _# json file containing delete item aws cli input_ +│ ├── img/dynamodb-crud-cli.png _# Architecture diagram_ +│ ├── README.md _# instructions file_ +│ └── update-item.json _# json file containing update item aws cli input_ +``` + +--- + +## Prerequisites +- Docker +- AWS CLI v2 +- Basic understanding of DynamoDB operations +- JSON files for sample data + +--- + +## Architecture Overview +

+ DynamoDB - CRUD + aws cli +

+ +Components: +- Local DynamoDB instance (Docker) +- AWS CLI for operations +- JSON files for data manipulation + +--- + +## Setup Steps + +1. Navigate to project directory: +```sh +cd dynamodb-crud-cli +``` + +2. Start DynamoDB locally: +```sh +docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local +``` + +3. Configure dummy credentials: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='eu-west-1' +``` + +4. Create table (CRUDLocalTable): +```sh +aws dynamodb create-table --endpoint-url http://localhost:8000 \ + --table-name CRUDLocalTable \ + --attribute-definitions AttributeName=Id,AttributeType=S \ + --key-schema AttributeName=Id,KeyType=HASH \ + --billing-mode PAY_PER_REQUEST +``` + +--- + +## CRUD Operations + +### Create item +```sh +aws dynamodb put-item --endpoint-url http://localhost:8000 \ + --table-name CRUDLocalTable \ + --item file://create-item.json \ + --return-consumed-capacity TOTAL \ + --return-item-collection-metrics SIZE +``` + +### Read table items +```sh +aws dynamodb scan --endpoint-url http://localhost:8000 \ + --table-name CRUDLocalTable +``` + +### Update initial item +```sh +aws dynamodb update-item --endpoint-url http://localhost:8000 \ + --table-name CRUDLocalTable \ + --key '{"Id": {"S": "123"}}' \ + --update-expression "SET #name = :n, age = :a" \ + --expression-attribute-names '{"#name": "name"}' \ + --expression-attribute-values file://update-item.json \ + --return-values ALL_NEW +``` + +### Delete item +```sh +aws dynamodb delete-item --endpoint-url http://localhost:8000 \ + --table-name CRUDLocalTable \ + --key file://delete-item.json +``` + + +--- + +## Additional Resources +- [DynamoDB Local Guide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) +- [AWS CLI DynamoDB Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/index.html) +- [Docker Container Documentation](https://docs.docker.com/reference/cli/docker/container/run/) + +[Top](#contents) + +--- diff --git a/local-test-samples/dynamodb-crud-cli/create-item.json b/local-test-samples/dynamodb-crud-cli/create-item.json new file mode 100755 index 00000000..cf086c10 --- /dev/null +++ b/local-test-samples/dynamodb-crud-cli/create-item.json @@ -0,0 +1,4 @@ +{ + "Id": {"S": "123"}, + "name": {"S": "Batman"} +} diff --git a/local-test-samples/dynamodb-crud-cli/delete-item.json b/local-test-samples/dynamodb-crud-cli/delete-item.json new file mode 100755 index 00000000..208c2239 --- /dev/null +++ b/local-test-samples/dynamodb-crud-cli/delete-item.json @@ -0,0 +1,3 @@ +{ + "Id": {"S": "123"} +} diff --git a/local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png b/local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png new file mode 100755 index 0000000000000000000000000000000000000000..35d64c933c8a602ed604b74f571846ea2dd3a047 GIT binary patch literal 56353 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 + AWS Lambda Functions with DynamoDB +

+ +Components: +- Lambda functions implementing CRUD operations +- Local DynamoDB instance running in Docker +- SAM CLI for local Lambda execution + +--- + +## Project Structure +``` +├── dynamodb-crud-lambda _# folder containing necessary code and template for Lambda DynamoDB CRUD operations_ +│ ├── events _# folder containing json files for Lambda DynamoDB CRUD operations_ +│ ├── img/dynamodb-crud-lambda.png _# Architecture diagram_ +│ ├── lambda_crud_src _# folder containing code for different CRUD Lambda functions_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ +``` + +--- + +## Prerequisites +- AWS SAM CLI +- Docker +- Python 3.9 +- Basic understanding of AWS Lambda and DynamoDB + +--- + +## Local Setup + +1. Navigate to project directory: +```sh +cd dynamodb-crud-lambda +``` + +2. Start DynamoDB locally: +```sh +docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local +``` + +3. Configure environment: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='eu-west-1' +``` + +--- + +## Lambda Function Operations + +### Initialize DynamoDB CRUDLocalTable Table +```sh +sam local invoke CRUDLambdaInitFunction \ + --docker-network host \ + --event events/lambda-init-event.json +``` + +### Create initial Item +```sh +sam local invoke CRUDLambdaCreateFunction \ + --docker-network host \ + --event events/lambda-create-event.json +``` + +### Read Item +```sh +sam local invoke CRUDLambdaReadFunction \ + --docker-network host \ + --event events/lambda-read-event.json +``` + +### Update Item +```sh +sam local invoke CRUDLambdaUpdateFunction \ + --docker-network host \ + --event events/lambda-update-event.json +``` + +### Delete Item +```sh +sam local invoke CRUDLambdaDeleteFunction \ + --docker-network host \ + --event events/lambda-delete-event.json +``` + +Each operation returns a JSON response indicating success or failure, along with relevant data. + +--- + +## Additional Resources +- [DynamoDB Local Documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) +- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) +- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) + +[Top](#contents) + +--- diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json new file mode 100755 index 00000000..1c47303f --- /dev/null +++ b/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json @@ -0,0 +1,3 @@ +{ + "body": "{\"Id\": \"123\", \"name\": \"Batman\"}" +} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json new file mode 100755 index 00000000..12090c66 --- /dev/null +++ b/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json @@ -0,0 +1,3 @@ +{ + "Id": "123" +} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json new file mode 100755 index 00000000..4ccb6ff4 --- /dev/null +++ b/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json @@ -0,0 +1,5 @@ +{ + "key1": "value1", + "key2": "value2", + "key3": "value3" +} \ No newline at end of file diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json new file mode 100755 index 00000000..12090c66 --- /dev/null +++ b/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json @@ -0,0 +1,3 @@ +{ + "Id": "123" +} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json new file mode 100755 index 00000000..074569d3 --- /dev/null +++ b/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json @@ -0,0 +1,3 @@ +{ + "body": "{\"Id\": \"123\", \"name\": \"Robin\"}" +} diff --git a/local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png b/local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png new file mode 100755 index 0000000000000000000000000000000000000000..97c6c748c03bd4e21a844b313a3ee421403fba7f GIT binary patch literal 49967 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 + AWS Step Functions with DynamoDB CRUD Operations +

+ +Components: +- Step Functions state machine for orchestration +- DynamoDB table for data storage +- CRUD operations implemented as state machine tasks + +--- + +## Project Structure +``` +├── dynamodb-crud-stepfunctions _# folder containing environment variables file and json file defining state machine implementing DynamoDB CRUD operations_ +│ ├── img/dynamodb-crud-stepfunctions.png _# Architecture diagram_ +│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ +│ ├── README.md _# instructions file_ +│ └── state-machine.json _# json file containing State machine definition_ +``` + +--- + +## Prerequisites +- Docker +- AWS CLI v2 +- Basic understanding of Step Functions and DynamoDB +- JSON for state machine definition + +--- + +## Local Setup + +1. Start DynamoDB: +```sh +docker run -d --network host \ + --name dynamodb -p 8000:8000 \ + amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb +``` + +2. Start Step Functions Local: +```sh +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt \ + amazon/aws-stepfunctions-local +``` + +3. Configure environment: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +4. Create state machine: +```sh +aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ + --name "CRUDDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole" \ + --region us-east-1 \ + --definition file://state-machine.json +``` + +--- + +## State Machine Operations + +### Initialize Table +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ + --input '{"TableName": "CRUDStepFunctions", "Operation": "Init"}' +``` + +### Create Item +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ + --input '{"TableName": "CRUDStepFunctions", "Operation": "Create", "ItemId": "125", "ItemName": "RobinOriginal"}' +``` + +### Read Item +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ + --input '{"TableName": "CRUDStepFunctions", "Operation": "Read", "ItemId": "125"}' +``` + +### Update Item +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ + --input '{"TableName": "CRUDStepFunctions", "Operation": "Update", "ItemId": "125", "ItemName": "RobinNew", "ItemAge": "56"}' +``` + +### Delete Item +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ + --input '{"TableName": "CRUDStepFunctions", "Operation": "Delete", "ItemId": "125"}' +``` + +To check execution status: +```sh +aws stepfunctions describe-execution \ + --endpoint http://localhost:8083 \ + --execution-arn "" +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [DynamoDB Local Documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) + +[Top](#contents) + diff --git a/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt b/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt new file mode 100755 index 00000000..2a80ded2 --- /dev/null +++ b/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,4 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 +DYNAMODB_ENDPOINT=http://127.0.0.1:8000 \ No newline at end of file diff --git a/local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png b/local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png new file mode 100755 index 0000000000000000000000000000000000000000..a5ddf9ab9f2ac9587055b4e971e0256deeb33fe9 GIT binary patch literal 47727 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 + +Components: +- Python Lambda function +- SAM CLI for local execution +- Test event for invocation + +--- + +## Project Structure + + +``` +├── lambda-sam-helloworld _# folder containing necessary code and template for Hello World Lambda_ +│ ├── events _# folder containing json files for Hello World Lambda input events_ +│ ├── img/lambda-sam-helloworld.png _# Architecture diagram_ +│ ├── lambda_helloworld_src _# folder containing code Hello World Lambda function_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ +``` + +--- + +## Prerequisites +- AWS SAM CLI +- Python 3.9 +- Docker +- Basic understanding of AWS Lambda + +--- + +## Local Testing + +1. Navigate to project directory: +```sh +cd lambda-sam-helloworld +``` + +2. Run the Lambda function locally: +```sh +sam local invoke LambdaHelloWorld \ + --event events/lambda-helloworld-event.json +``` + +Expected response: +```json +{ + "statusCode": 200, + "body": "{\"message\": \"Hello World! This is local Run!\"}" +} +``` + +--- + +## Understanding the Output + +The local invocation provides detailed execution information: +- Request ID tracking +- Initialization duration +- Execution duration +- Memory usage +- Container information + +Sample output details: +``` +START RequestId: 7a563ee5-369d-4d7f-bcf8-c3cac3cac68f Version: $LATEST +END RequestId: da370caa-cd29-4d9b-abf9-e81ef7f6f769 +REPORT RequestId: da370caa-cd29-4d9b-abf9-e81ef7f6f769 +Init Duration: 0.03 ms +Duration: 44.43 ms +Billed Duration: 45 ms +Memory Size: 128 MB +Max Memory Used: 128 MB +``` + +--- + +## Additional Resources +- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) +- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) +- [SAM Local Lambda Testing Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) + +[Top](#contents) + diff --git a/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json b/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json new file mode 100755 index 00000000..4ccb6ff4 --- /dev/null +++ b/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json @@ -0,0 +1,5 @@ +{ + "key1": "value1", + "key2": "value2", + "key3": "value3" +} \ No newline at end of file diff --git a/local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png b/local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png new file mode 100755 index 0000000000000000000000000000000000000000..05a982171532bf5ea377998fb6f0f23076a65cf8 GIT binary patch literal 38055 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 z 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 + AWS Lambda with Custom Layers +

+ +Components: +- Python Lambda function +- Custom Lambda layer with dependencies +- SAM CLI for local building and execution + +--- + +## Project Structure +``` +├── lambda-sam-layers _# folder containing necessary code and template for Lambda Layers implementation_ +│ ├── custom-lambda-layer _# folder containing python dependencies (requirements.txt)_ +│ ├── events _# folder containing json files for Lambda Layers input events_ +│ ├── img/lambda-sam-layers.png _# Architecture diagram_ +│ ├── lambda_layers_src _# folder containing source code for Lambda Layers function_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ +``` + +--- + +## Prerequisites +- AWS SAM CLI +- Python 3.9 +- pip package manager +- Docker +- zip utilities + +--- + +## Build and Test Process + +1. Navigate to project directory: +```sh +cd lambda-sam-layers +``` + +2. Build the Lambda layer: +```sh +sam build LambdaLayersLayer \ + --use-container \ + --build-image amazon/aws-sam-cli-build-image-python3.9 +``` + +3. Test the function with layer: +```sh +sam local invoke LambdaLayersFunction \ + --event events/lambda-layers-event.json +``` + +Expected output will include: +``` +Version of requests library: 2.31.0 +... +[Function response with GitHub API endpoints] +``` + +--- + +## Understanding Lambda Layers + +Lambda layers provide: +- Shared code and dependencies across functions +- Reduced deployment package size +- Simplified dependency management +- Consistent runtime environment + +Layer structure: +``` +python/ # Python runtime directory + └── lib/ # Python packages + └── python3.9/ + └── site-packages/ + └── [dependencies] +``` + +Build process: +1. Resolves layer dependencies +2. Packages dependencies in correct structure +3. Makes layer available to local Lambda function + +--- + +## Additional Resources +- [SAM CLI Layer Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-layers.html) +- [AWS Lambda Layers Guide](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) +- [SAM Local Testing Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) + +[Top](#contents) + diff --git a/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt b/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt new file mode 100755 index 00000000..2c24336e --- /dev/null +++ b/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt @@ -0,0 +1 @@ +requests==2.31.0 diff --git a/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json b/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json new file mode 100755 index 00000000..4ccb6ff4 --- /dev/null +++ b/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json @@ -0,0 +1,5 @@ +{ + "key1": "value1", + "key2": "value2", + "key3": "value3" +} \ No newline at end of file diff --git a/local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png b/local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png new file mode 100755 index 0000000000000000000000000000000000000000..05b57993a8e1f88b4be3226c480c5ea6eb5e7a7b GIT binary patch literal 68083 zcmeFYWmH_vvOf%j-~^Wh861KQ?h+(O5;O#NcXyi*++BhuIKd&f26sYmcL*}TAcG78 ze{#-wlH9fK`|jPR5fG4K9zIlP zC=WfDq(A);5U>=jq@dylk&UHk#*>`KjNt z!-g(*U^`==d%?4D!QHVXA5;XH&=(BDFY6En5_g78q>$zk6W-3{v%h#m*on;EDMh6= z&^R%Hhmi6adc1w1@VQR-OujK9>e`I*7fs_@utiCbg@E(NMM3#3o;m|pGy90dCuhF?3i zm=olvp`mm9K!;|ujbPkGNiqROse*C&`&W!-4(WT{=|~bg9P0gC$^!xT!K@k_u=_Xt z_v~IxL!Sk04GYW$rA*#&cDITSex$7E;t5-QHA$OCHG_!IOP`DALu79DT#8@{T3oLD zEl}_gYZxPw^X65cZ62=_o~J*S2ErsVaOBFMf9 zQs+<#^Bj+23C0Q!c%W}R5-UCzXK}%AwDx<$_<95|)t`w`yOBs$C`qnKB_9=}Nw6XJ zQc?HnlY+GoG0=HHrXuQHuQ7VHE-N;=@_~uj)6=y%30Rv~#OJwc0xf+1EvokP93sG% zF(Rlm6X^+{@54nz!BP-4MYXYNk1{KblVO~YdnRiNLr;m9u0aX_x0Et+ao9Nz6%0Xe zvJZ>k4;9c?!R6BqZk*-}ov)r=zaUkNDj>~V$AJPPF_wrE>3 z(S$zvpBVc8=orJX$L3u=L0CflB4w5tOok-8L^Rgj3tt4*>wtIDp((dm1CHvvocFSrT4_8Ue2_Dk9 zBii&mLm(55;WaLwQz4?%cTz%8U;1ua{pfD%MOM7c&T!&8Gu3t7gC+2-%5o|&JGpWp zs&%hs-dWyUT`U8$gVdPMfZ6p~x4VL;xuYQm{a5x&A4kBfruVCh#ZK-oy2{cNJ|0yC#Vus zSkeKWaae_t$F*xhsevC2*)@?PB%W(x^S#HY!ahWvUh{Fp<#})Jh@bg5wUfy4(Nb_} z5rGJ5+ZylHdnz>a&$MrP5IA3!;iC3DUZ-l8!^IEip&68WmJlw7N6$r~6_rF|6OUnv zsv3Cvo?Jqah9u5?>&YqZp3I%}N*?JeDK{09sb>_C&dTi1V*2vlet+YNq8-LAp^>|7 zNz{x}E~}KA`kl-Y^@beWjZhd+&A~l_w`nNCit+jz7l$ApfaTjz6?54Wn-AmH&t?KH z438aY#(L%(X?4GS_C#Xuu~<8U5P3eM-l*17oC!Z$I z#E^vNPVj_atg~%O-cx}NUrU-S7j-Feu*Y_Td;?>Hc*B1K-B=jHSsg1)^E8fJPAs1( z;eC=4GlLxsn3jT3kKQd|C4qOK(3HfPLok??o+N&Fz-O?3Kx1%x0KlY8$Pz(9A3>8l zQdpLIn9HNW{#NKYpm;%z>Qhl|LN*(=?35Qa?dky@l3v*5Rc{n9laF!O%$(ANk7}u266zLT*U-EjYZeshu{2*oD zzjCo~v9u^}_e*Lq|LaQC<6?bOOcSlzt1{gg<{60@g&CMiUtVKgPhLvw7cw`uDKhxP z&E1VL93Kw9;Xhv`tHS<(<%~5$%0s%u--8XtHo>xZX3r~PGRLj$@%7Sj{b#}is_eaS z(6f26-e;qn3szIsQx>+yo_Z)0R z`BJln*)4L+FVmlX3{HPO>Yi5j(T}5nQ;7q}wd&x%`oa2J^+1)T!-VmKjg__FgmZ;i z`8H6UkR|3_%CtL7S@7~X_SC9X|k*!|So( zWtP>0PqToPm=(blpSBi;xi`29P{&%PhbdxZG}>>p$(71k znMx6cZOE2KHUsPkL}_n|NR8jPzA+n_oO7ynF&XdW+$`(`NBd9!a66dq>_6Cj;QCA- zAD@=4nRvzUoWsgawLiZ)aYvy!$u^Bu-u?}B0bSvt;$8+hk5~8Bm|DZs##F{z+rp7_ zXQMCcQ%fbXMVy7`g%q!7^XUt~3NWTHd^Pl&AJ=;slRm*B>oV&u@g0_J#X5(y`Yiq_ z>xtETc`TERE_&u3DF*6V>ajtLC2_4IPexZ#;0%n+7D|dBd)1>m5odkv6}*xd9$9bi z=D~BV$wjj#HO5-~Sd~x7%+H6qw_Aw9j3aV64M^^OHZa>IuQCCMuZY!%TeCh2oPB2n zYyHqFuLWBGbKqqhjVCRFSLExNDNW`sB%^);vBD1hzNY>+w<(W_F;iH7>XOz}dE>dM zjOFG;W=75s!|MdBoF?%%=`mA9?ijW*7K}%Yy&&ZUE3X^ax>My->ev7szLTfp^tlO< z1!Dzf1po~+4c%M!rH1WQXyu6JeEpE$^vrb6^p9!&*{kvr{nEC2NARW_!R&OUMI}({ zem2C4j6JC-#cokt>NyZUU z=Bvj$Hto_f@Jed)@z7Y;@T$Ay7=*gdv_cqN7OCG=jh5S=WLXum7Tn4plQx=5U%Q1s&lZO zv+OUnw6mfPn%{KZBpPQ?023_4>Rq&WpK7nw)y`he0++*DC;YkBcfdL&4MO&j_8!ZF zZD*&c9}L8n^gLr7E4SKb;5Dc+IA8qhU~ZsLpZvi^z zoD@-RKlRD&AJa<|OMcr8gB@;SsiODa-`=)Bk24JB+OHru_u|?WZ|es6GYTbdqs z2IwJZO8Ln!BTTp=Y=|NX`QJKH2n}Or3cm|R8%J5p6D!y)2n4;IviMPXOF55}{Dp;d z{tna6Hc+znwF1Y(0YlnMQ{G%b0fG6UjfQ}Ph>!5-p@sPHix6ssfc&pE0s_Os--k2F zC!~LRkYYYP`rW4bH3(Z5QF*up*-A~*Ra4=OkcopGyOF7bu^GFko#U??5JWtM9-4M$ zu0~XzcDD8|LY|^D|GGoyq5Z2HNJI6nTU>2KX*3m-siYj7&8Ya;IoLU9#4xC+s6?Di z&4pB@W&T6{@SiA+rK_u>5D@6$;lb{~&F+eVU{CdHUL#`%H&;;_nqLq4{qt`=U9HUjJjveWKg)VpAn?}+ zkdvJQ_`i{vd0PE{ko_9@H`%||_3y`t{F+Qi$=S;6f#F|liE)bj>jD3x?+<$-zh)6q zw(>Ny)s(ifGqZR3MZ?F-BLe(iNB&nztv{5!JpY68uaW;yiU5Df=`XqcTPgqQeb9;+ zh6wPthQ%=6Iv_r%{RM)&w1k=`;$fRd8vVT1&b3}*eNF3u_39}xdR^Yilt<2(=AA~o~kx1s4t!}z#=Zyo@mcA>7H;IK%^BOA|CGVE1>elr)#32cdSS> zRDUkT7hNjCAJD?y|8sT9A=8n|cG1!PMT&?n3x4!B6)j$zBRP6vl=KVa-xnQu9nPO= z-lIJXb~(|#;llsT0pbJIl-OSnr{TayMDLI17?k@n%>$JR>^CJl`T(Zt>8NYO#_@eabU!+G5_R|0(R=a=s+c*pp(c}c|@hy32|Bx1W z{}Eo5`~xfe=wH+S?1KRCgTXtsI7nO={E+}(_ya2p0qWd;kuIY@aD2&D{Qs-)KDEh% zbzQl!r!0BMW^C!~77s4VbHqu&wt>t%&khZ`VeFvIs8 zsZi*m+bnst6zb*T$VgO{xcI|(R(&X?n0bFpR@HUzOFjX(V*>EYUOU^JPCe~<5}CbuG2ACxE@Ly)3^T~%Ty#|ZjtdbCk81m?!OWjjt@K%gKUa7!rw&HxIWU$bY%w_i+EC!1Oq7qibzQZKhB!Z6QfRPtB%u?DL6Ms-C zys?o0L8bI1P5m@ZGt?0@!HC$=op{4^#aw?AV_@NA?;_y`=8zdOdV}U3xjc2m+@% z%ENGFkQfgOfZo}DTX_xN+^5@53WUKr9LxjpeHk4$^XzDmjzJj|q|{5{Fk%Q(WTa3> ztdo1F=%$rYJ?Ls`H}@%k_cq1hMn?AoAE38&bF3wbHlkd8G5COR!OZ7=b#c|I0|NE< z9IMyyW^{P@{5lon6A52f^q&ppkd>(W3lTSiJJ&9jSQ^zMBZr|y_h5%Z|0phYR(37a zT$b!y^Tr4tY(P&SW5@O4anGCqev(q?&y4qRCfBIqr-jg3t4e?#AX>E&u!T14`>>YhAz3TfL#^Y-J4p}`*VJZ-XWgzRK2ZySpjw1AU*-cPsX% za}B%x)7%iXOU3a`GnW`kZ=Wk|eSP;@V#)>K&o%OW457Bk{WUrW6{C3p4{K=LoP z=qh_IRUC%y2KjxXVLO&<tlacDmyP;NPczb-Q~-DDJDwNSOk`xcL*61oS@*U~`T&$sK~> zc#J5{lj&b-!`6>MwApdlPAdJw!)KV0jrWkZQ2HEmMjXK!ZSiPk)$e{skp|G=ZbHXO z@7~vrN}D?Hg!A+Be82A5wjX5IwU@WNl<8Xai(-&i_et#8`S{W&^Hb8pDV+DTi=dmy zY*U3OCU?id>)M?Agk3N0MqRj0#~BQ|uhBm81kfe7+k?CT?46PT*3(BxtE$~SoL-v! zAkdcEl{YcA)_=LPeu4ltu8ld50BBa_y*X^s<30rryq}CUB6O$}pC4iTVo_LRcfTSE zy{PmrfX|8F>x*>O-0|>Vu6T5mvsT_%Qp>Vj9OD6BXBo++}68!glyok>^HS?D}FcbzQ`!PiIjZz>_j;F zu5F_LSmV(S7hlY||F#GQU9I!K%S%(y`urD8xghD7^xy@$0KEn^7CU-GB4JdxO(IbZ zmA8U2sFP9*exQh(&bu~friFNSb~KD+wxz)lF)yLP|9q3w&XMGZM-2A90ROa6hcPS?%hd=9Pa&tUO@TMN?UB?s|K!2MeA(EL^8hw#azW!uZgF^}?Q!;U3}o12*-rd~R($V_yU;_8u{l#2whi<`T-iSb%n-%+ zp5Bl=xH|AQ&<`=lgaGNRgj6N-tIf%cB$m|;JpNMrDo%K_6!TcryF-+Kzz@(T#YdRz za8>UBolsEBJM%fd=?>U?ZwzyXZ1DiyI*uO!s&&N9J;W}?>SoEIgFOZ}xfy=!{m;K% zH#>M??|9wP!hV1+J7WvC(fu)g@5Wzl&8Z|^i4V|YNh6DX{p{PS*DPR>wqNNcNkkOH zg2~P-H4hQ%JAbloO;{C^ZRT5(e)i(WU3zI{ea1=L$eDvb`~C&^vusS!EMe}AU=*KN z6i@0wWy_3#gXd;68pLqy0rLIy2bz!91%NIdC!d}GYOY6i?Hh97O$Jqf`;r{D9P<3k z%(S31-@p}0?uWwOZ}_)+01C8zI1GBzvz$Nh+(YdCdU5vgP|?Sni>RWc37M5dOKdEn znUp8Un|hZ^kxeAnc_379iBj`mHZ+ORi0e28zOS=t-lkZydkJ%Bnzk8RCkk!F@_&R( zN3z1=1z6$s;~Zv|29vaV7A=RMdfs>%hNk|g6m*tW(l)@7o{#t^O18B^(vcJx1F~Kf z7$0cI##Sb z-xKdNUCI0^oPy)OmFMZ*yh)q23a40&fRTAfI6}djp4;mi&KJ#+3unVHLk9iu(r1j!e7JVp9$0~$% z#S7fN zoltRMHD)}NbqQMQhWgzf3NOVNp8&++TA+<)(rq*p za?aJwAir7AWjPeQ3RQA)*nBGNvy-{XEyycqh5F?14DWS&fq7l6HRz7yxGoQea=6B0 zF5|~vu|NEmyyyD27CItPjVoH9VSWevpuF+&f!RCACT7rTAdaZ2j1{k}!r^wL(Y}0?-d5MJypD$urADBP4?icn9j4#j#Sv}I zb^EhlJiOt1enYQXO5-u)XSe%7zu0=B|G1&4v-?dIg`h7fLot~(;l;Iix(9q#yc?e6 za2aZ`FGl#+NeKMr6Jm9e+9ZA1W7&&Fk!!&{Fl+2Ikpat9>#i6c7Pd20d z3?=^?7wFjPJ%MmAW7_oz)-k{D=xhhZad~_HQR@NIkpX!#dDOdw28Y`$+Q1yh^Uv2~Wv z;2|G+jWlU0q&Vl-kOePD_Bl;^>Sk??8c(ukXMNtmbFNH*zKV-FSVQQ`Od(r-ja4s#?R~Q8lSqVlZNbjZ`*&jI(v!7XZQ5DU;oHHJoVhRi@kv1cV2+961gJH zG5F$G+tzg!Oh^#=J}YIuMHJt`N3sRk6;6c#YpFAG%$%InaDaKk;a zvZ0D9i_XZ-g;|v0f^A{#Kt&^D=N0JKn*}M07I%9y)x2mO8-g$VzcoQ3zZQYs|C6zA z_Yl}2V1kpWRiEQ&wwmokv+CLZ( zv{~}O_@?(4xp68NO0(x`QA1;@fp=nR94_WeLt&2$U?cTy(2wSfJ3i2&oU8_&&hG7=~BVVyZ$@<|yZnbL0!x($G+&X6Rw5I=IE&em*{nO$2xH>)Eg$Y#8Y3 zxYul@0Y01y_?Y4b2^wJpaOe(=`6-CbH2kC0hm)o7FOSGzjV)p%YSeMMPAt7q1Mh0! zJ#5xfSOQwsgJdV$FCV{!7Vb*DU-$netjIp+04g(SSQfX7Y(#F2fMGnR3AK3Dab6xD z!oSE?JE%UJ-RdxL2g?}M_gu~&UHg`5Zt??wygNDRk#)E*-d^NjAQm$`8=WEw>BNws z)A6M0JKU5F*Xn}&Xb2VgeFW2$N79iKaNdef{hZ-G{_!d7ZdI#p*Pz8%99qLr2)25< z^5Q2=IrcZ`NH+oWSbwsxvbMBUilB|dt$Bsv{%axmvoX}6FFZ8%)ZoDY}}0e89zCxSyFwT3z= z?X5%tRr(B?3Tzq-dCo_;7J|4}j;|Ih)<*f^DQy=sFD_rn~KItL}usSwA_ZDVdlFK$tzdW*t<;XZTzyfK^XA;vW9 zOQ~G36$(G+&kjWp!G@(5l$3oETFq;3o_<7LQ)_V$F7iGxsmGzsXes0XY)VfSE)s^D z+wsW6%Q@}Q5JKKrcKIl|fr0JcJJlo=d`$gOlmcomzk=uAxv-EXXLi!>zk{9VC9)f8 zeR=thJ-$G$=MG?dPEMd4L+pHBWp#YV1NgKj6~#7x(2kKBaQP9OZ7(@RFCnWU%PAkv zkIC6Ii+aT&9Ug2v-c5KlKpRYzN(aFER$#XCQfzo16U>A)&VB)2ne_7+-_-!82p55S zBw3a%2A^yN!#(E}{Dd)D?l*x8?kEJA26HY`s!ac&7!{IEPA4&O43S9tI}{wf#nmNF z8$fHR#q81k9A<@1y+dC&w=Bm5l#qw!r731^dExq!L_Uj^#&w%=lrh2h%GFX7%W`{` zE)VQ%oOQAMQgPnTLYpsE11%*ag#3&?0tBj@M?JQk52tU(vlv*DjAs5TRah?&U`I_J zNDbr$8XffupO{`y6fuBt#JYrDnvIOyDI|X&SAKpXVb~!rMGco&o{ZA z5?`pOV9Nd(*P~RJu4zT-keFK7W(afYhU(qMsiU+dPDP_qGspjH9$#e)r)I& z@@I6pLy_)3Au=M~oIP@VvSZmA!^`>~HuK|5N)0D*x}L3o!j2|0 z)SZ>=&df-=ktQ@YtHzfT09&nqfz7tqevajFJ;r8eZzwix1VNfQ<8o@+QE2|j2!Da%x+S* zD3K=$uO7_^urU9lBMvBXeTSp21%WEEvs$%oY{lyr5wZr>HC0&1Tm z=NNhcbEfe;$sTWM@#r|nN}VkC{d?KAh)B_5DX+n@c@w0LPEU28T)mb2v;CzFd639T zhFwd%zuHl;{81vQ&x0mRIxx}xIX^wXbCk?OmYqkj=C3SE^+WkU0NU~%;Wv7Rbz1%^ zHEq)Ucd}nb{9l><*EAkL=fB(Ozo_vGBL54HX#W=-N`T#~Poe%E4OXku;BuKyZPP;e zcfUQJt|C(O@ZOvDzRZVsBJCJK+wWg)G@xG?5koe-zaOT)Qg$&}us=#u?*1o6G0gZ? z0^cE1Uj8fbGy1Cpu9ZI>^C$kXbwO(Gcgk>h;r6e9l`rN6Xy79FC^3&hygAh#ZBA&= z@R`vv9VwU6rjQ#~gagHc-`26vDQ5f?9#y?WE6WA2TybpS(-Z{@D3K9{jckU#ZF4>O z%r zyT{gP|MLC;Y>MwU9Pn};Tc&yWrj)JX#7j-!e+Y))zRZdbh{-c{>2BQrFqWhtNBl~jf$Md*)Z%BYJ6I~%fLx40dLSvSx{`C zu}IK0-3Gzcn{`@5bTn`F7>wtA3Jybcg*=;F%rsnRUK|N4pwQus!0J)Xs6Hg zjuD*8JR-kxk>DJU=iN(?KrMNuL`J^|{{)IB@Q(=4h}==7fjtlR7J@|*qudxYn3#Q4 zNe_4bI9`zdw4@xE5D51;;HaE_eXXK=L3{mrG4E6@S>vJ=^Dg-e_weqUPHB*_P8b4X zsD9UO{zVI8necs8d8WAiFlJF%OS3Ut{|zODWEf0DS}D(AR0b8sZ;Pu+ry`Ob*?+T- z4MuA!p7@}tQ+qab@UAx+C3yM9AH}BXiET#S{4-lJTmxNNgLX;Y)yodaoV_AM>IKUe zYZqSW!EY~Ln^O~Cx6oz=iB7e-m^ee7V^5QJ9((P&FOAIin?J@_JWE z+rn(#W4SOk)5%AY{Kk@2mel9FgO@df7B^bOIP`a-&xcvtgzo6#eu)h-_gxl^3+sWkEn2 zgbjH5ChLITr+7I1degsiwB@lmb_K(4Gnrq^pYbT;plM-9%-7&BvS!L&CNqzf)I4 zP`sY?l#P118DlSV(9QxF&#eomYpH=rrI83tnMcgFZMp@#RIRia9x7(Wf_W)%XDf_BJ3xAi7?EAA-AIb$@4CJ2x}4_%O&Z>p21u8DL0T!V5zDmNud zQMuc{6r(pO-WTbtRaJSuFpZ36h_cH;Y@P6oTwY?Z++%QA?LaEy3+PeSrTRwX^mp^^ zgr_!eZ+6Umf0rHQ<@E*qOuGoH@F0_5jckNti!HM~;QU_b-GpM!Z6>b|&YT*-B`^DB zS!6)p$MC{BhN{pC-Cn&WQDqf-p zxF-)FsPm185n*CZ+^Wrm)XMh+(;eCap3&=)Gx4EE7q>2Fmm#lSUzn5kCYR}{_#TBl zaz`r;&b9&=sh6fgO=He`6^}RAo(rU_VZsZRaeZjPZRo)9`DBR-qU)00E4X^F4V1h7 zz)z7^i&E@W(9>%DZPahAQ@)4Y_T6rZ(YrFW9nG<-AHoc}#!r4$)qfxFH0mC)r^|%m)SfeT5a!&0I*0D>z=ayfrQ28&Z(tQ%OeY8Gw(MvFJ zibix6;NSUDNQJ&S0dq);3JZ>weQ@?7>&JG2y4snyieTL{N|Y{Fb5{Q@gY($>^<4Tf#KvbNg+z{Oiy13n!w>&a*{8_7J!o;VMOeNOD^Q!=`FpYE>d>O?$&sURV$3pqh^c1C}q%o8e zg9|gI8>iRAing+{#lk!Xhn({VWL3-fd(it`O9+iju+EI#y1%SH!*fdc?2T$qY28RP zUHu1#lA2o7sz(z*I@mpNsv-(`KSm#IgTj^XJvTtF=k>R`4k56lR*RbD~Ely`-Z6~#NLO-9_UWKQCF*!!VY_6(0% zV?LmDqpH(RkMeeTT@ASx_zM(f88F6r&*Ok6P+}z}2+4rKB>k&@$~W#N}lN)vYW3=3jU}9u!bz<|l$*+~*ZHn#$C*$Ais#iTzQy_e-C> zQ_X%V}lO{(aK z$m@BkPg;w@e91@Eh#`p1m|~jiq-IW|dW^F{rER}^oAnX*WVQ-R>;(mTUU&l{B6!D( z(5dsGyulbgMMrN_u{@jP9capqkXNh32O21W%&{3ak zk^779H4?~QJ?wBboBV8%H`WFdr6Y=t>1ez@Dfq)ZBYt*@x-$*;?sbkjQ#v$#$_022 zf7j96F-GAag=hDxIgGYNoUCVe`w78JJL>MX{mb=o)F$9G+DrL6RbuVLOao6s;6W5p zNunt}lx?G*LxvGdk5nKTi#i@YoqYBz1Ir^b7!|AmF&D%!TE~ZeGhKc$3X0IS50N9J zY_?KzPC8I(D&a=yR+iJtAvm^~O+Bwd;tGWMk+`wY8NKXKOww&L zkg%G^s>pQr`igl2dmj5dxd1|N8M6f-1@V_9TKfdvy<3efUCs3Y{A7sEW6PoC5`HBv z<&F%NS+yF*AS{BI;!L(n<0Pz^QQVlZ`;dxtIS|A*-lr26#QIO}MXZKeyUz$ko6KnU z6mM=JTE2b)v&IXErhPa^IN5GrbK)|EdWfUV%AlP|2vYoD>EAO|9xi{$QtGO#7J$`Y}=P4cIX?CLjyV=m{D{MtT^% zr3_r_LZhR!UN_%HAm`P`5mp1nCQV^rUl_N-v^yX;c(E0PWVk zCU4EdPy*==$#WD%CN5R3A1q3)c?q9c-fSby^N;BBT*(NnOy%-2Qh7!yv3Xy`C#Z-t&(}s>o@<3^QJPCp5L=S zPPU6^q88l*+}>z?&3yYRe`l~1yQOrd=v1Q|G+j8$l01oC&&)RZfMj2f_(@W`I1Cc; z9%EFZ1yr~-@1A@sMVe@je12$5Zq(z#k-eK*tkMS~6I^Y;k?TtH@jQ%-y1~yN`5c?n zG!ds&@WFaIQL1tbI7No*yONsDEwd~zUjx3aebH;WOORy2eYq`W9WpQ5_&K(Q(BgT~ zc`51CDW2YDUUq<|t~>N1*RL(J;?-(e=La{r{-z{y%l8Mes9vIzA%+yb+Mb`#yPVv% zS!F0wsXjbEt_$nz`}H2aK>evNI`YQVz%Z`}U&h;zAk8#^iWe@qxlkX10wq ziZh`UpNygJ84U@U>x=z_%6vDXu_gY~m2$gcJz66^3AZykQHiho+U7r!1}K3_k}vlqzJ@;1M8O(q2z-1* zG-6ADA+gT*_SUzm-pV|EZQisZs>Jw3o;{LqXVrmhgY2Kxc$F5IH2iY3*cXK~T;x)Sd ziYUgEDfuE^h;69T+B{bvV3E1$V#}F)Z+{5 z#AXo>yvuXv3$fb=w!fwOa1z2ZTfgJb3A%c|Lr>en^0?%l{-a{PTgQs(JALCL%Q>Cr z7jn$K{9{2@(cf2>)*KZItqROo@H*8G6t+-VIHsTQ<-|^HSJ|?mJzqKa2{SoVu9Al_ z@(bRY@+R%Ck=+~Bm_PU889~RfHV#wa+q_<3|M6x^C`r1&&b)=1)(q(SHhnT-+emU^ zu1L--n~W*}YdIZw!O}Nkgx@zf8@idB8V zs@**Iu+%uS>3H9{q4HG3!vV^Gr$}Ayg&!$ahs5WmAkP zOm0|qA%StnrDRrKaBuM?ZEivNVdN97zQHjcCR2gdp5vJ7mDq)5A|3blV(YZh>o1-T zExR;Y$cDhbqo&Z^;zG_H3p4U7PkO)0K5dyx$(&P-M6(rnC>%yxlo7M1BPc338IHJx zCJb!vVWO(@Zy~yuznr#ZFGam*B;%r$A!P=+=HRUIdNesFCmPosez|w+c=zx%Y%ZG; z?XNWz{ioiwiZ?kJE^UNc$XKLgKS6{QTlU{2M}F1kyk>bjza{dJqBuuQY*+(h5zQ*RZQ>V+1Bc(RchvLj zP8~=hx*)q64h|;hG!(5nHmk{eJBdiaKI^%a=R7qH(?PI=kNg4v09}x08K{)=eD! zTBi>iX2i`+v|7Zhu$PXT4-#*a2?ncjw)id(&0@E+`@h{WhMFO85wQEa_&9Zq%Rkmg zN4TW8$1dQkqd`8qOK@*xT{vZb2e(uvAR^GlREA7(G|h#KcN#>t1Z2vQ58ru^%?WQFWbN3Rl7w_BLLN{-8Ipe8wYle-M4nfsOi!Ht#ZuFD~@SxcsrON zG&AST>xoh!x~FD8Z|uh~T5)O(K%<}Ti46f4cy6F8XX+5&%K+d}s{C9vA}g+4cE4xE z@$r0!5<^?dT{6MDIO7%ETn_i@XLd3aguOT1&my@;;pSCGs%LsbwF9iFU8oE49w1_! zm(stb(IJ4eZMq%mCeHf}F($z&>4F>4uW!OlfZQ4g$Q+(*yZ+iai9+Jfh+`WV-cE6q z&cEpI%V#49|7=h@3GlbEXZKFJA@K`yiV{#jEvkCb67U_f+xWYIS?Y}c6&)qx{HNEj ztv#eW8lO@35^Yu6NW5YPUDs%Fw)>BqrW%lK23EtMavo3i$Q zsoI^Y6=D8&9Ve3%n&88PbMhVa*HeliBS69Acw>#2P9dc32EbV!Yu8VD*)}F+>ubE| zxGxb-N}G`;Mz>ciRc#0cw>clSK3+>Ey~Bu_DPhSw9dnO?-jn2`RfOmnIl{ zBzF5QMhcEbHePst`qY-S0ObrtNBtEtJI(`$4tJ~Qvy&i-Go9vt+jJyHrq$z382Tvl zv?CtRO_g_i^t#*M`I!VHal~$?Z}(lat+DOp@SRuP6Wu)R)gL$cV@Y>6+8PHGERxU0 z*vJZ}U)k2^R6g}zYG~+9*KB=1S_HCzGh`I!cqf-OJ(?;1QLVS6WKI2rskIAP_ZIf> z;)tx|TF}dddS1xSr?_TD@2d_$Bg5MTq*jVNW1vS!N$#3M{>hWs5_oBoD^Fgg@0tPR zn%5-doaCr~8smOevPqmA^nyN44CwXyu5=^q)6VYJ{n@*qSr!Jymd7YQ^9*$@OsWXPJw*?uahym0sd`~1%N&|2-5HzUBuDWiRK!B3!}p-u zKtSbcJq`7T8ox+>x>f!m%lKJZ8Oc%mXw2PAEDd7E#X7Gyz_pcu!Xj7=cthZ?>V0#Q z0UjU&O_Q1U-h}Kfp0+hi__&HMHDY%(`itx_JPPt^dmU`rbG(pHJjFG0?$MZ2Qs5m; zetAKueNA1fHsn@p7i(lF<(9N$i!>L>l+cLz*=m;Q^Oo`D&|;K4J6;XaQa=F-4x#9+$AH-megxS?i8f$<_+cwI5}lgBi(!|>ea z!&mE)sg$u;<}^onk;QLo+TK>}(jP8y-pRDK&M5J1zGfQ-hvuY>nd3-qU@oP+?DNnt24|KLXa%iA`r28sB_odiuNukC?YoYrZR z{>y2vM?4Vc@ffbF(pe{3|Ce5a?G0uHmd^GYMx3OO%J3j9haJ%K4 zKSFI4|7t2Lte*l;l{Ej?DY|OMqsd8Ouof*!k}Ad@t28zTdFUHCsxo0PeOrjxw4~So zad#EzlRS5KRlyTYO<9}sp;pW4eGRjw75O4H2%2?wX3kr{?M0>uJ5`ZWC9Pk7hgzEU zxo7!DRVOOd1HiK|P>E6rE32yTqvVgtV)VibF&_w<4JzIP<*2pARYKjI0_8Mk&j>rX zkaam!eU&?>#MpJy#%Uq|z@B=Om0&TZ2s<*6VU@pDt3GsFOr=YNT`jcNp(fm~C^J)p z%%S?TAO`$x>Hc+z^)}`}oZWqf{nm8?LF&eY-cjPY!fsBl;5jiAY_-~bT_F~l~Q?E8;wjz7L>~gJU2%%Z;zH&abPQiPMBW+eIK^2 z69=}9{vxMbC^s33(DYDW`!Q_lGW%|17`uZ^H_`2kf8FIMf_DY$xK_Kl<$tpP#vBDi z`9^JIFvZ#6BGW%NhVGacVs39^&r;k@#>ALgzPBEbRFWwvYlbMaTThW$3(K2pecT~h0%Plz)0IqWOj%| z2Y=_5ZWq1W7^9Bu){8n94$e}0I05Z*WHRk8L!or^!_cPv$?bs^cECZ5B@Qj6;C zc---ABj1i$)HwnycP;zsn=;`gaq8rO{lR5LXsCWtU&rG{d8hnXi+a_S)sr@}wm?9|UzmxMV7M2Y|b~YIk%r_@;%58z13G^7L@I zq|R;9`Xj87gXhbm{fLw8?tRSY(*u~zIugukNwA5f?M-JZ7%6 z`gWCChMF(`fE)=9ruav;k2-9QoA0`IpmkI95RE$m}hNf5eZ*#3vXomNiv^{oWURzwN=WVWLj^$cd(VHLD%;6ew5O1dt(# zY;5PFzhxHGl}8%It6HgiObn{e9UVz&X@D}`Fj@K%@N);(OPXb6*Qu6k==KAEFQoe| zT7IxrL|@2`7)ZT{Dc3E=@+OWSa;gyu;lml(JCd)Wo**2d!nr(EHK@iQ5?*yO7xNVj zm%$L;vN!~E(7!%4FIeNV>UAo`lCgL|6(F?u5r>`^GH*w1jGEZ?U=LAwhp{mpZMzvlIm@ z(m2GFavukm5g`G)a0`80m(xIPXT>t97G+oI5r0{R*GLf>rFvbM(&#pY6kmH=R z*_KFmg8gK=*a->9zZu7S^UR}n)gI3#b zV&4+Ohui;9Hf-9>|PrwIFXunph0SyPm*i#`!6qQz^D-t72 z9=p90D;PnDW2cWL0o%-cmIQZ7rmrfvpl$k>4|%s=9S?nWjo{r5v@;x@T$rz2HMt2{ zDZ@st=a(zwVhIKO6T4>#`OYg^-b>X-b{W>^({#;~V1J+=7)LTTk0V6Offc%V+fL-| z?$SZPw1h(2`%Gb2v-lT%wm#Cy!(eKnF8DR&Tv44Tw;9%o25fw{xyEb%o#guy*xG4= z&}l2d+gC4HoriKegD*LT-v~Rp9m7&qXu)^b!tuEx_c8JoT(~dxsOmn!P|A?;O>nEw z9`fOff;Q-}kLH|Mi#c%4==^Z!oy=d9rfwS0F2BEe`O0omOe8R3-I`hr4L;GoiLsh} z!9Zf}{XxnqK)Uboy)y;(qx46AC9>ZV8m(~fOH7mLa!0HzIHF7BVJky%j_oo{wAZTP zC&31c*CsztRho5&iU|T8#Qw0adgSzEw>XR#`a35Rfr~(bMg@HA((%I^SSp4n>hqQ) z!13azzW8=3&~rm9T1}AshI|;t(o3K_CHWO|W-Q0oCEkdBO(t!ihV*0T2I!f2Hi;`f z@lGEz+mET;tMK;6F+hPjXgQmZ&kO_th2}X{syJrDP*eT_PfqUczP!3=O=3f7b^`~- zOu&d$jW-vQ))9yjBjuMLC8|dpYmH|XxXnqctGA-}j;K~D!aIz6#Ej?vsTZvprb$$O zFQq91E~M$9k=-i{c2tEbh+zaBo;4LX_S>mglGQ@@YJ65-T+`iCCI*REI9Ib$^_3og zo7jbYbyJ($=L$1_gFCC}!7Pa35}&HM#DB&nqKhL5BDTUhd9Dso2JrPfX#^o|%si=S0m$XGXy z!hbF2Xxb!mId6D~&NNX%#R{`pf_rHOU#0b8C8AA#N(g$tQuyOS`&-3_B>#lkQH>xw zVZu9zP`2i12OWCj^K1-)b=x_g=9w7Pg_yQt(VJ^kD@1w?;G?Pq^#Tte`o%1N>4>4b z(0+-lgQDGlz>;8`-I zYwXN0oHNg`Lf9UKr1{9NjSqgIs;t*UYBJIl2Iv4$g4*rSC{yDQhPYmZLA5uQK=-*C zoN+82LR*&Cabvrf^<*36<2k*6cj?cLmN{Ib7Gih@*{`_4z)9yNfdMBty* zUfkKm#9!S}$!rw;2-&RBL7X<@X5g+NrIIV(Q~WHPP#Z|^r0M85`Y9grOVGM{n6(B5 zl<@x$>ZBw;{FE+)5IYW_bjJB@DSJ_Sj1OaNvPhgBsFzV-XC;@f{d8KTX8YV;k-=py zbx2UN-yi=aEP+ zF*Ye9nT7be7eKX?Fd`7Cv8Nprz_nlly4T;%3k!%SY53)Bto?EihsSAUJDm#=@a#A4 z__JpD*u5~Dv75S?)N^YG&O-m$s-`oJoeV}8T>2*Yml1b;UB?^9tdc z{nMSLPAd}fAYll?f_7dlrVmAMNT!45g~kqR61nK$Pw0r;fGk8OroT(xnt!=y-r;qlowxZ z*CHse+g9+9Tiu+IHn!T~Wyd$Ry5Y#_XNz9jpH@t?>A47`yo$uK5fL22;p^FlU;St% ze;f7D2t_QXM)XItR^TZ8nX5K$O4FB*TNv!M$Ixe3E8)Bb2%`D)G*$XahtSLa?+lss zpSvi)kNL-h40tX6ge>oogAxk6=BXEZ7s|7()WCiAw`4Z8Xpt8tAnQY~zwyzNChjTV zJmw^y@jTxOKcv4(q>E{{>t3acJ*mC!{kC=fH{@YHcYWYTUb()xg8pz;DyAP(YsfPc zD24scXw7lq`R4wb4h`w0tX%25mgmc4#jUUW=uHerr0Zvm_|_!#td|W!VYbPC64q_5 z5^k}T1J1J^q@j#xF$5|2;`yNR-8|OU)r{q+$qbph%74G^-A~7} zYcHOa8oh3Sn(Xf61UVdl%u&~or&lxiAbcm=&ejGM0<;ht74K(@!qN?!w}m$>WkR$u zB*Q%hZnSGGYy?VV!(1$o(UYB<-GuxX1y288j|WZjh$%*d4|6+>tfH+tuhoo4<1=$h zB(aM3XWpS>NR0~zG<%R_Tk7hf?%f%nJE5q z;QdTTw%VYrO0L`0fzn8qlz?%%flX)$>hGKg_vnJ0J*26eYzuEQr3%*_$%y*qkP#Up z>UIOj=872%&AQ1t3iLL_;^yrh-zNH-4SuB01|q$*G2yra;ust>R(IE}4+Y-(;oe|c z*w~_q&+&wFwKHpeif5is3frdQa+U@{!E2B76fT4FOiwF^MOjPf33W#g4E)|-=GoKs zU`lrx4Hs|Nrx`-c_22v!g$Okl{IxB~19#VKnrxXL4I$%>Eh{=+Mbmljm`{Ia)NRa2 zXzFn4Q%en6Iq$Q-w1rdp+O=T)uCFHH4P#%7q7Ld`OwD}YHaRqmV)~a{G|+Pl+y2Hdg3G3LIqSoba54&nL9<2B z{|dYnv+Kb*JG>J2eFRwR9%T)_l#piE9-uCG?Qllr=#AHYI_zBE8>YK|;W>}^yN+## zy5gJBshtp=*2yDx>!D)I$nGj-o|Rk2t%>AL&PHxY$rT*(*1`zDD93r=-L=;n?h_ky zJ}^4S_Gndm(K-Vs!nLUOucn^GMBh*&_n9)VC9G zb^DFhln=7&3hMKH-Z>)yP(b}Qq2$GK05)_~Golw8IwU-xFt}+y97IPox zEanNR_|+O1s#c61PRw-VAdgXkwL@=r71M*Y*zOb2*DfRL*N)i#fTI*~3?RWiA+QXY z7g#Xxk3s!OVIG&Nh#+jvj(x7!>h#AbE^L>O3ubIa$F)h@#p|Yln9r@40`&__*F0Qv zQ$qO*W{ro^{a<(XlN2)8V&f;7g+khlBKA&46UxzObAeoBJk%v|%zV5sqBqgjP98-a z*t;Dz+uYJ%1I;M@Wwz#L@9buN0C5tx~ zY}&vkOy;fkh6v8TMYyc+iz%FZe|c3b^^C^9%Me)F-2R>z08My68}lL2)zwu53k8<` zqHf<1316|*{+N)<#~cK!&xgKdrVlb=W}}HIHlSTklHe`^ki1pl-w3I&%;!0GhVqog z5lUVGSjMAul*@^NrgLOFq$k{igggOynN)Oq`pVGh0)B_Vm`@l&{>a|H9c1|74o4b` zT&~u_y3h&5f>p(|XYqtO7ip(`O-y*CcPPlJ9Gbxu_im8(NIO7iIq^d`EBG&6^|rE5 z`3PurFUHi#{lnp14ru^c&M7&t%}9tf8p{wJgM!q^ZZ$r$ zcRc-`*>$_?a%Q_}0n>Fc9)LtgB67{{*n1n95Bd@}-+`6(qjlfqx}RK6l631**$O%E zIZoIec`lsW*|J&*ZZOLl7W6M55St4;B)H>-?Ko-T?z@|sP!Q*PM{MVi+)t(y@JQ() zJ`$Q8Aa5>uE3j4vgp3k4iL5AK-8nc!MpP-IiQ?Ef z4|s{#IV^_l1!cqP;I8LWESCGuY|>NPCS3IPY&`&?7_Jxh#RUpm@t$off)yV(?+aX@ z*#0_XdpAxGC_uB*A9ftK6|z>hZzc@^V-9b`8TwjbeliT#CkIfmJm?dR5W!AF3sf4V$ zvl4f!uhFJu1bH36gfy)SYnlW+UFd6>&yl?>5#_r){d{sl>5bdgU4SuGB8jM)BA^$x zr9ax;%$O+CBIusf=M}?qufsM79W)j2u`pNm-0}glX@C}`_0aV)-qr& z)Iz96xN<1U-os9R5=vf<`TqUXK+w=DZpvt0t<)t|Noqb%p9`xtq{XND_W?(o|4_w3 z(Nk!iU!;Y%R3pikg_jLp7@m-OST#2UsP#9)6AbF72nl95u?_*^^w2s>(Vf0JjF6WX z4D+qwPX~18cXU7b#HiFH8JQ+46OW0ahcz>$w#vG{*xV>QqtjjQX8Jz}8-qrB@z<)p z1`q%9X6H$6tM}PQeT_`iMrI!z;}l64150p&h@HrurtFZz7c`?M0A(ta8ir;4Kgew! zEssX@(8;>NaxVMgdt$s*Q+jX?*9Msh0sfsI&ycjnY{d+M%bcMjPkap)6)jMf<7;Mh z$Q3-1f)7^-joYM8g&;K}~7z2QE`&=gq=wrA{~P3b))vf9|~t zzds!ueuM$`C0;Ts&J!;Y=}oZg&9V|ihhl}J&z^OH!8ah9!>3tJSal{Ywr7J*POcR1 z_5x+bWmaj5$N&A>IbMf(4?7y@BXB#M<9NDbs0~Hw10H5LAAnYW1g<|j0(KRz0~Wg9 zBE}0vB5(ASQAm@}%6_lug_dlRAD=>s?2~%Kdaq9rVzypI9}{TXKl6kkr+AG@14wFT z2RouVLae_h7vh>;`mcj&(efG9aa}J*gxK3Xw=&c~5Z(dUem1%o{B$^^J-y_E-FCww zO=;#v(r@#B=|F!v;`QIe4W!IEyAZ^09fCmKI6MOU%Q58N%dgpZ9vg{>y~Gdkek+QF z$UU|kETLo0*M@bpiIFrR!}X5%V!yx4J`}Y#uUOyb05K?XRM>&Pl6p0;pgy*JB@+g37dA+f|ck7zfavUYLf=SRcPpSZof+v4A8TI9%%FY6^c zY$`Z@-Ab2MrbaZuT;}}R0aUQZCTOJFZD zYfS7llST9lkFslW>}{VK=}RGh9bL}+*mq~A5|po2bp7S~j5mW|hJ(JQ_BjMSTj8aGOW&TF46 z$kA9q(gauMWXrl2rq_tV3+)PcMm&%Of$lh*cHqf5xXrfJJdPEcRqQS4@eYQ4NINxQZv&9z2M1Gx56qzz z6T|r*zEYSZmd@L}T1mifUlUY4dl{B`pq^EJ>i4_E{7_&ECd)Eo@yYhS=D;(vi)UPy zDJ;F0RzzNQOfqt7$*BdV%MlIX%)hcSZL{Y>*Sk!An=~pU=ePBF{K!~A;hk6EA67Mj{hH2k zewTicJS1z}SC1z;lXyAEPxQSNtF{!Mahp05Zzh(hrIJu7b+>_XWMrBpx)#=QLa}2A zwrkk*2K8E*PyGrua|U9f2MzJ5gjAHklpzs-N$4Et7TFk)z1H>5$>H)aa3?-LIvcBb zae9+XXd7!iq(ayKu}-3+>&ruQ(oea+S(sBR!@yQx`WA6t`JWRz$3d~b7SpKUUVr#1 z?H8`MEu-HO0)pe34`XL$EGA!Rb#Kw_d3e35U(t@S&Wb_`gOwb!mCa*91Au!v%`+>i z1%sh=s0G7SD0bV??avHiuC08mG>OC0~~=mz&v@H?w@^I%dy%YG$2QOOQLL4+7q zzyEJLW$4Ti@oUUS`8FCGBCmcgP(a)ME_(gym4=dHny>5g2N~KQ2FBs}8>!O`T#O#1 z{DQmbV5)s%7dWwneXerzQC4<+c~k8CFgL6L47=gCH5<$9fnH|v>phkY<=KJ4Q!*XL zA@VL<4-{#flw3WlC~=z`Nm~FVFzN8pPc4vLw0Ju!AY#Kum$0wJ z&YRtA#DOd&bLR+?#O6T1lAr5q73hCcda!n0(YSQFT2GmY~(H)n+U3z7Xf3S$* zgPsFehbvnjYZvG+L;{9s?m^^9?HlCRf%hy8ti5O4svz4HS6b_4-xfYmA!D}E5x7*!dG_Af8|35#@h)a zujp)?H(E5cV^HYAq>x#6MF!4oHjRWA6pk9)D(-fid{7E`+$_xX4GUx;3zC4a4m?Vj z)L9!5Cn8`fqh(bCAidf$n_W{l;a|m+NE$>vNT`jL@9i>)OJ?hP#eCaL6k(9!KMP~a z!-Sd8OhQs^|KCf{6ravv*?h5T0vA59I*Ry;^Q(xmGzG)GwEvq7s$c< zm21xc6zaq*=(22nE;%DIqM$V&m0HkV+Z>9RZ03!66n3Oa#}_~AGesf%%5Tl;?eKqG z{u|O6srriFU$Sa$>Gb5m*Zu&wr#x7ZH>MS=$!@mYdp#mr?F-49u@x0ALkr`zXRS48 z+93?>1l+)T?-e@3@m|t_Tj;|5?$V9_hFArpo2}}*dFO5q? z+ROkjylUv~E+g0y6w#_XeS=&P-fs}MBNcsm0$tEHkp`%br65_OzqyKhm6tK>lLUg8 zP%vdyk9ab2$O;-jGx(l;loY>FB?Z0?lrY&|I+6d}R;_!<^fK_p>>}ySF+7V*q_Lh@ z|9PiAJ$|74+=H2+AbY>j5zDgn7qG~Ytop0#hS;P~6~BY|aEf@q8uCtbZaw!t9ehkh zmqM{d0$FW6^ALTm9K^6LtEjbB@StXzx4#?LZW?Rz1jS>5A6O=@N*QFclj#U%EHYtDBB45B5fVl3_h4PZXJ|y>1BTMo4Oi*Lv)u0#3 z@~6=Az63lvHQhNDqjaPHcx!dFQ8uB66vc_gnZ-ij&_6Rkp zHx}NEn=<;?rxhNs3!GBA>WC9nng-$mj_j_b_F%t~f?&7^2=B(h{A6$}ghPY6es2Oh zt<{crOk=Nin!9Optgk9ZEIU94hCIPKcBL$AJ>k??CB@Xj1Gw zr|xY8^3|XBH57mKur&uXm1tg%LY$rU0Cb>73VI`2^;Uwt{TRjwEZ!sx^EwjzoC+ok z7u|pvmYxi_MXJgcx@s(PRrA5PY#(sqsY_}1a#@T@fwoK=Q_kcR0;OlKxelT)5x0d`N#+eZ&do zIW0y-0qq zq~+Z}jNb9JT-!eMF8aqN#0Ol(kE2;Zfx8=SSbMr1|K;ki~51oTJ z+pXH$=Yse(8|VAwmY+R&L2T9B%MS$26E66D1uLG{!uLp8tv1rn5I-y4tygP2LFdgF zRd~dT4e&hswzjQ;(FMjk<%u}x9!%2VEfDDw8t!t$u6d2=xW$lMm5Il!C z;as>U4aKJPh*LVDgRC!2R;F^9jdfB|z<$r3ezV?*-C=89Zk{mb-E=K>jbR1J4JiqM z0jVWCeW&DeiO_9qX|;(E10;3${E`YDH+mB|+f;LVT1lO(LrWCQExmspZmJJG!=Bcu zmlHbuXgs5C4qp2}r#rj_h~!6m>y1^%Xstx2IekqnhZqv0E$S>dFP>B+!#`7BB*p)I zi6C_v)-E!<*x$*6A4{;nMX-#!KQ^JkAyG>WsaB^4qL$FOPT_`6i#$8dLhNXdMnk{! zY4{yS8WW%s3+NBD!y|B(&MLdyDc;!B=&j-Hz2*F(UW-mCrT+GcN9DiD{ON}r{ttJ) zw3;+?kFQCqPwo$9NsRU46ZV81m$&tIaAe>IvSbBQO^aYM{hqZ4u!>4VrLi!@C?jvS zpY*~t!)(VdWrA= z-;bY)IkqTEH2>KxL^RGLg^9vM3D{?+t0S2Y@<~C-`|Rp+2(UOBeX%TGBUV1sM+kS6 zETZNt$?~4)Tj=;e$XnuuFwjkU+ao{vj2}l+yhEC$Ln-ldZBoFN(i!}bQ|Gngm_Ra` zGpl+3x1|_@W0!w%mDBq23i?E$yElAS0zPo$fnwD(>aY~uwV|_XM7)Mtz&BZP_6Sb{ zB-0md*M@X{y2eMT?6n{e$XgmYvL@n<_M`PXoy0$we6yi+>!zy*%(e7pr->xF%!d61 z1=tFV4fqaJM=l-P_}9w12LkS(NQnkbH#%lrrlpl2N-GxuX??~k^Y#hbld%9+cbw$9 z$(;&@Td`}jTS`k>T1`hNIg7S9K{orSmawL2JG8Hv4SDuc%hGxbmm_GJKU)`t)GsFO zsT*nYd>y$MhHwCKJ>|#Y7zFG!T<(4U0d$A`7& z`;~EqqcW#QAX=GL3R3QnVUBK`SQfkpT5ZPapwNKABIe40b(|wwXm{{`w8j^>dAY-0 zArF7uT^gm+b)1~d{p1*``o$)Rt|I}yq;(8nx-g}jc>Ibod`|L*%?0lLXsavE zHKWjbyK!R8A-Vxf{PaeW5Ny9oK1L)Bc-rxbY!?H`=~Qj$bOrTKZi{^pc;pP=9G+7T zctHPic39#TOTLYP9`ah^6_>LH^|4QO$0xGYMeS~gUx#+-TN0X__Jn~HkefIDAXr)H z%f6u<6}O;m{q}!Q$&n1%2KiB={W5S|F0h}TDbO(aeu$kn)eK>uuUA(iHld5@cAvzM zn;h*_S91#OHZL}s{|eT}KV;K=$0ybom->KX)3DP^X1kR3KBI`a{LMSwbKW}PChNO{ z^x{VS=>6Ek}Y03ZddrK&F(x!HIe}~@~86xe^`jiV9V7y73v^rhC z9fCOR_M2l;lywFf{*E$(WsziEK>EkRJ-K@(GuecX40^j_yj$P`^-c#j9$UHdx}B1 zs^LA5-vmB*o~zD2O)onPxn(L_H&5`$FZR?gCn^^$ap40X)b+ZTf5t+Rz|v}2(SmxjBIHno=ty9m?fpwaFwQXa4c{sP&@#6J8L+Q}{#@}7_^bX9E zZoJ6tg+saDY%)E9w2IA3i8KJGT;#0o#Io2GAHRx8yNE~pt^{rR%DP^0eb%9W$KONAa}g%V}odRxC63bxsn>$q_+?(pkD*(@i9eD;~|nir_>gctg{9nyOIop$?%nK&wp1@Eskxh$CBY*s1p#-WD2TW8?{&OIr02fxy1t^_q*yEACRsEA7xdVD_OKS9dOMFEE%ZOfo_pzRvUaMQMtQBk>N~rJKpz2GsShL_vsn}K%i3@EsmC%X34_3> zrPHB(A=zK1xsf8(x?nra|k@{M)Ikn>DvEA=A#vl2JWKo_%|<)0pzx zgU8)n5#UKN!BCy`A6ee27GE*FiJ2UyRg#Q{Qn>k6(#WNeuECmgID6CMke041#WbRU zpTOd9*L<);K2v(qS=zWFhIcmLf<~!8A(?%aT7@dNO)7i*Hu*szwV@DS#F`n#UlSZdiC!()#(FEx9PY??1)PsH`{ds^+M^ zbPgFUVnFSL{3&G17!BJVX20omlEa>gEtLcJPy)*-!65;>uiAp8p;Q^^qek0kB1H$I zmGI9i1DEV1Ms4pabereClOc`4Ao5>zIM{89WcAO!T{-XI*h^E&@%6)%M*9-I4c9$R!rz4H!ZhHE8% z%dW*>^(P?l#`q&#GR_>ZtD#C^xwYq~WPY|4#_Yf!VrJ}yQL9KB z;i~K&|M2f*i5z>Elia~-Gt22}NMGg`-_O&nedb~r>a`L^-ic{$v6<>h@H5=$i3^4a zb;x}%9Os~|5sq!8X*jTl)9#zjYlvE5i$m9Jo8D=($3Pc9(DBsGe2dabl(z&|SHpLc z2WgGDhJZ4uv$P}?rE|P#57B}=!J>ollM+a!zc=PCM_d!Sd-ONl3C28@!c2mNrg))Q z>kQD%2yk$mfh)?5zl>&1R1nW%psDQEi2MK4G!=pQm>T;QpfGOeGuRC1F10yN?AECI z7|vG1UrwPp=^Toaq#K(w08vaMHh=e73RZPkIY%}`tKnz6;f>~ZL7(*#DHb=nmO8Rs zM!fY@vqjd{8dP&ayKz6kRXD$?bW_lIm|6{WV0j)BHB*wK)_aV*|MpX`UE}~IXs>JM z`5hTT1cOtD-=-|Zqq=vfpvP?U8h@;kCSkipTD_=;Wmxs1HAYN1N@+P%OB)dGB_(&p zelV9hhj@}9!Ji#RpLzp|h+Wh{rSlwIyY6 zuVM{Sp4l#(gyKRPyX-lLEbqIVc3y7E>6-PuFWno=EnYq=Y;5@siwzhKebLj`=4N3H zWymb&<-3l54zd=IPK)nitKg8L%sTcv^Ua-hVq7c<qCf)4I=a}Jmqw*`v#1~%w z!l~n>LU4b}S~NZmWQ~tNHmy0+6nrnmSwcZn-fAYbs1USOfrw0GP4MW3nG(eEsWV)s z$MF)KCDTFHpod2R1(CqHqLOveH{hI5(N`HxIm2bEt8t(g5{^zvGq^SUN88ljKlnPRH~R5~e|71ZiTb zLW zw(kkuAnh77S(I{Qeq-ezG#t7 z=`SR?)t-mg!2+bp{UrvX6>LqlI;m@VYdpDZM>1janYwcg+Dzt}lL07RF>FkPKB~alrJcPM0+h*hiPh}&XB-u_CK(`nql~{Dq$2*-gsU^I*!$q3! zaYKE%xkDwE)_lT)#%5f7Tj~0i<&yktiA%4z%7Z|0kyO8Zff6o1QI}?^$##Yfzm^BR zBrJ~+^F%5tPZ>}Z(W>pgU#LssAHSw5uurKKXaQUZZ=y91_Ma?(2G*CAQ@L`@YcvPP zU%Mh$&a4+S#j)x8$=%ZhrY8J*y|zwUq4Ow<3-hOkzg!nN8Lh`_c+(d+p>@%A`8?Hmp+xza~ws-M(XEIt_`~5FgaXw$gae{Elb^=SHz4OAk2=X``pz}>pxIN z532V7rAg(5eS#yCvE+$}v~S=`hqbL_rv=|O8=%%9NvAjp8e#)THD*iFjb2`ooFDR8 zC&!LWFu}xg*%+)~_aqwSu^&|i@s~r{#L_-0P7#Jw)^j^rJ2Ol;0=1O3-;;l8a@7x# zfrcGC)@+NXmEAXe7i5r*uCDdZ8yB?KNmqXJ0-qA_0rjQ51DFk`o&sXX6(@L-zN|0% z(Aec2zK(cG%ZlYe&;CWbLybN>9MyL}ou4cUEc9?$AZ`Hh}9D{I?Vj@e8(D5pDH8?ns5Juv)?_mUzIRGv}l?wauG5F{?Vs!)xeZ097)TDY&K`WB(MFz;$vMC(Zw?D0Gc`(PptLX8yXU%o(TOhw7Hy z!ScB1hV&{wL2(Rf!Suy?;=cy{G6Hhq7<(#gb3=R91HZG&-{-C{Nm}l%?S7KrXwlyn z-hzh%x~3O79kf7TVxvXDrP=6P4s_<)dwrj&iy|W@?0)kKTLAZ%e*N7{lCR7O!PN)R zw-McsNt0HbGE$|*a%U$@kFn{Q(R(-!ZN6rv0JDz%mj`~gC}CS`g|6tg;BsgeO^6ec zkE0uGIWRuYE4&!}d$`do6{bh$Z>;=3eWL)-gS_7Q9Jn|Oxoa?w#T|6d4l=vEs^22V ze?NaaceXZ5&61=}o-A&Wk}EhKl`D9*B&<*W^YW`%?hn6&mH!v z>5rng3Fw5H&JssbXSVzH1!h`=4!--$Ss8YXTlS$eacysQmzYz49GtL4s}{yWXD&Lv z6>og7^ZxeA7|iULl25h?ZKp2d8_m$R!1+LtoU#o&%kYXQ4^1lMtN7=QIfT#VKfg_? zzlphXC#BGh+7R5H^D2mIEI3=njrSq{V%uj-%kPpUQwP7{FzSWc4TjiKe@$GhGhYC= zAd2REAy?lxO+7O-0B4?VP)i1CzmZ7|bjahqm>l6$&pI8--7;Pmp6@e<2Y0X+VV_yc zUfkYA;_U!0ccw9B)khTzf+d=G?(k*Zn9}Std&1m~*1d6idU2qvZ*3ET=tgNd`jG`C zmy5dEiDAG{R={Y;$uiW_bc+Yk59@8eCUbJwjBBk}lQ~VA7N;X~n_N6ID+grG1fKLB zj}($OaCt~#3?$AKJEDguXQJei)O9MbKsbS$OB#|6$MC9gB^xVfDP)x%bw{`UU?Wh$ z=T&EkKYB6xwRhGF=51yDlezW9&RJoR@FbwUI$_691S;8YrkZ{Q#{J6p`78lC5J&z` zCUhX#V*Q7M{)I)W$GsM(&xI4GgN9?2 zbHqeqL5v2d$LeCf+5-A=9D`lZ{~oay-Qk?wkS5mjhWe3+#BFGSU6!M+FuWMT5{1Vf ztul)=`{&0u#4=JT0_$JU{`OZ?0OYb)i#z)W(r+G6TU0*k_ZkUZ6a)Jo*Y&*RnTzq0 z5}p^H6z5!b8TF4-`uOMtmWUzMDlZ3we0o}30}ZDh>gm2oheSLxNLms&uOTTlv`TrXSrUXeTTOJL4$hw3_1C){Q)+Lz z3Ys#VWQgL|^{&sHN~LK)vzw&46lRwiVF)DJnhKWEjLiD@i4;;2(xZz`!q!DH6y=*| z&#Qu4mue49t>)Q&t2WzCUId1!5nHgm37-q;*9M*BT>XoEomnbz(XfPJgGmORln>_G zgAFL0W3fe)H7^*eYj>7vuV6YM?mX9f-S=|BiE~vX8=EJVfNk_p!8|KWVhtt~_$Q*? zPckRjxF_u^Oe?Fh*pdGa$V@r>-X&w!>n%RIURS5^aWC!ULDq?(ni!2jLG#D5Cf$hH z=f^P6O8w>F@Mm#lC^(tszX^-e65rSvl3kQC^Z~PO5k7}KcGA~r>uCY0Vs#F)WW@!Bv*#Hirz!6}#5#eMK8F z<*&R7@{gUgcB>&J)90#?kMK@XV<8j1RXKyOjeK8Rek@B8!%}Bs8y0P^o3J_8?Ksu} zylHD{K;bYcV7Y?&-Ef5>f1JwO;}ijZZeouo-&`=nsmfAmY$XBdDQm_MX^nxtn={57@K#`&wPZdKs#MT%76LB9^Z&sh!Q9edRZN%ZmdHYbpK@Q)e00W*4mM zKp;32FK)%HxE6PJcMnc*hXMtPd(qZ4*DAFD9;;VhM_7v&o1K4l}%sgVr8Qs-@e3XEMf*0v_ zHS5L;62G(B<)p}X14m?b^!go0_V%T)-!JT};&3Rl(cSZZo`=2cHn>z&7K#5JLrlzO z+^U+GC!P?53O9QOaDP30)sJreyS$-H7?ksq$~&!WON@$I7%U018?*g3?Rshw4oG`# zuk2+Hu8qb3N!B?1I+^1;CS!^2vY47mMhzp4@@{`v*uRu~tN&RLKWCjw?p~@|Rb;vx z){QzyxZ$u-r?BdIPliZfXM|nT9v$B)6{bo_1x$Q*#6z8ZdXqC_nm6xt=4{maY?Bjs zLgoB$b+jkx@)}gO<{7M`!iztBzmePTk4kdQFGR9Gwd%PCfi?BIoWN?)V0H6(5Gln| zpCF?KGhmg|eJo&vhH$dp0ocQm28NWk3sHHPiuB;;ddYbuY!COh+W(#4?fQ#5l7w1Y zckCzUGLzlSmM#2bpK*#oPn?bD&NL)ZidajxM?T4?QqVoqXfBVDpmH+k5x#vDaYia$ z%!=#XDcDolX1t!4c(H0A++eyZ4Ag=y5(a157@8I=mQ&Xqniy|0QD-nlEqp={sHtYl z81w3`mPlpi^oTW_r`+C?2Z(MA&Jq?Hnl2Bz{%ejm<^tGnNO%izHG!o#1-%K7^=AuD zrZ!Z|&qe~#z>jHN}sE&uSp#ZA+p~ALHB%l9)sLe)W>=ad|eo~2eE32n^jKLeL zC~=r%&O1CJ&>Oj^d}PcUx^uaQW=M%sbA4)L@6Vi|22R*+t;aBVPA>l;{?3B&wD5wI zw}#4Fj}9a!=gl*Xu;CFrSkFTaA94{`*DC3B?M`uTTcXbMBlgE0C))ZsV%9WdU*RLa3Qp31Lfn}p7e49Z(X3T z$?NC$(<1|h_|*^?pZEMg<8edU<9y2Fj+l^8`vjU}K5^!1hhrpP*AEHC{?J0U2Y@WDLmcfZXBS$8{c&D)aXpTv6KuQvGKrosbcMPWsnAvLp=Wc#u` zYotqa8-)n&sF|#_hFE^Bo&D+ZRC1g1BcE_etUbk`V%%qMsVtd?_-9@P!vWOb#;vAo zTh{>QX-LBjvMF<=wX@tkg>qVSLB;VI%EI`!T_dte9x87?zkzxTa;gyOgj3RQ3A;GQ z67Jz2>M%_Dt8>lEImaCZ>yFRp3CoR5y&CTJ3QEA&g%`d3>JO&>i8E1GQ*7ilShEkp z2*Yyds8$B_JEq7LtWOzvvHrv)q6R1$^#?IbpUhpB`G<>#FZ73B@iIt=bp36tCnYE( zbT}hh#Nq-rsmj$E_LYMjOf@TqCbJqQKUM+zg|Z;FW~Xnnh;Dlp*aZ-yk2uN)(AFYj z%9>;(erZu??}eatp3W?>=8R@9Sl&b3ecMDBKO68=^2YyTS3LjNmHn+z+0SOsTWS(? z5`v-s(1EXBj{QML@qTI?t~b^( zesUyIk#r()IrbC&VPVY17CvJnuj(@USG;MeJ2{u6a;!z@@M`j_Q{661*qyJY1S*y} zvxc95{vne<$T@O;#%vQTg|2k{Pi;)=h&sygA_j-mBPUy+rCDpE(TLp(AcPclvv61^`a@W_%} z)29{TX~Iv4zNa_D-bt;`8T1n*u;9gYGrBGnE~jbRzbiLh_j@X&*I{d!vo~TE7g48VZ)N1OT!dAf7r^4&_UZRo8RLaEHV{6Y#l24}Sdl00f0H zCBkx!L;^$y9_hqM7yP6&5|?m7_L(dLm4Q4NS3dxcXSK>#ij}`Cft)B(v&r(p z!1u-p|20rSL)Q*gqT9lFbS<~Cj?@H_T!r0Kkce)AfE{mBr`_zoF9$oas~Nk}J`?Ab zwTat}f{;FH=!k>vm19a2H};8|+*nuQzEBcP#chcYf`uuER-bL369rEH4jI28g|ffu z7lHmr6eQ=nrRBA9AGb0Ed{UCfhu z5#3@M0?n8g=JTXOLgAw4gP=q#m2ntJ?20PPYk0c0HYJDOtn+%iwU`WSXFpqBwp_Eb z`IX^byn1#Bju5OQQbeUeisGd_DW)1;fgGHyI2~>u6FsLDl5FJI{4t@i*v zeHy$g^Q>|+^u{H0A_5aRB3R@$aK7fT@l!4r0RfW94scNU$a0W7#}h9g7MqYkEg1)! zpWFi~x#KJpuHiTL$fhuySN^K{okfBkZU2>zFz_H~@?IGTmLQ>$iuFgoLS=T=tXS>V zOvwWCG$btP$?b_e2)V>~98=m$bZ|@g;p6l}#W(YQeesIv-+zcTQEvu~Z!B1XH-KOjs0(_+n+v8L7;5k1J1cH8XWVtl$(w&d$w zBYm$yoB?oK84HiTvr^kNkr`AoAV*+}%uQ-GbaFVJ1OiqAMCVtsW1BgiMu`x3CA19I zY!Xgk_4aSSw2z<*3K$?r+FYe2(<5EScbAOgICo7o(dvXk_5 zPlh;%gic1o*LmWOclK9rHG>Osa@6hX*-JeUD~Rn57M9N9eO#?mmUMiLmQk4G5W?iX zr{k*GRu$EBqkGuBK24HuIv2dc3YIIqsR;%vdK+m5uF6Q04SwTiPlC)x;WbLZKT*ie9MDE5{qZ)H-cYS?st$L7O0)^Own)`< zt}RJW{&o)d0g_jrI~_z4Kjrhmtj6p$EU=_gM2$PRgNjmyz6g{g}<`m3P?JA5ky0;_1t{@2hr0 zK&_uVAV)gz6`s_m1`0J;D{G*OHHT{axoY%9kNEj!pw!}yeEjU-PIuz8VNb0p2mDUO z#oNGUGug(`aZRGiHIPJ{JNW{3@Fn9{S~u>&G4E8sR97(A_M!x_7Y-Q8kuxf5Z!(5g z6j8vLl?PY{+uq|1Ojmnj?&y)^NpsKq=0@CU~|6AZaky@0p zZ7SVM(a>-!ZH}yL2P)Z`#2j0m;n^5CP`m<|_@PQQ=qwsSW<+_=S% zz*5I7kNqGGhCmviTo;RKr_l>@f1+D+e}%ZD&+A-=)xRHhaX+1mYUTFkS2Y)|p!2p% z7tE6zdvM~JD&lQ7*Ivhu9^uIbG2)cI;0rTeUlw@5snXnx_r!JGBDhzeTOAn(t z*aro$jd`PrPVPG$qnw^(qO2_2H)s0!ymi46hZ>ZksZZYVZ=5K8Ba6znD%$zYhPS%y zhqrbYnl9;6En~aXdFRcmosgVYLMfomyx)@@2MiHpCE8{%U;%Pq`EIU#D=+~?SjxsR zKvjb7e#ZR>j~#`H@gh_1N;CBL;S!XrF!(ob;rJj!nQ@0laH}|834;8>(5!;u^56} z9tG-5S0ZAXruvj$Ij3~GZK&W?=8NX!#GT`k_QCSyMiBz##%KUVI*nQ+PM^Cw?w|{L#9qj}zC8{PmdD^JH`EtcYgLQkdy_ZzYH)96#0Ea8#yhSa9 zW7w37q~p@U!t&gsW5@(3wO5m&0S7NC`%yZ1y!9$4ULnvNqdp7Wuid^dhDwV*4f?Oh(q4*h> zrHhk&*}A4Bgo8XMAwWzfHmD6B^k&-aWByV9uIQ<{5^r#z&vSmIL6P|tQp`I5YD2xF zg}^_B!_}cL(;tYH-cYOpe}_8<$U#WJsTnN&B=HMZJBL2dOmSW>OvlV?!$c&cA_adb zYCUi1rk)S`Yx_^5`WD4RLSoxbCg=D}*hu2nt8PkE!SQ#A^J2>_ix-vpk9DHT6PmBo z%5T{GktArACM-E`ksB5JIwECURE*BtYLc6WA5I&QQB@FP#~_a5)4SiYy*qRibYs=ZOa$i zzFJ_r_vmh0Tm;zF#9rt`bZi2clL*b92mZ#c`k^>Mh?@UZ)k)vRa*3TUt{I!dvEy#? zITai6YexW&w-`${Z>XtAzdkW%Ma?T<%;qV9pV#rwv`N|=lD`jx}x`w{nwq;es?tn^5Z?3?~Z>SXk^b8 zM7!VA=5J_v9j*G!m8?mb~UwsD@=5^mFDw_xMIrnzwTolsrsc856I$n4<6Qf@t_5J1Q1 z^%rw74bO2ufcz`N>Z8+M>8!M8-AwHZSy1fQkPY4I$byk#JwRD^_6#M1x-OskPDu*~ zoTZi9gEVObTUm)D+=2K#w^MU_VaRq*xI zf}IQrxme*06ox?Ww8HX^a9c;Ra!v99D$>?=+PRKYqP9OCf8dM4fm#VXJa{5+%Hd ze}(3R<@1`GyUNXt@sGL|-o?HJHo|6ZZB71_NB3ftQ-LhoA9pp57AXI|Pi&^P&%LHt zybV<|)W^GJ{Ume2r9VPCM0fdjl7UH!HVu~U=;3mz+R-961FLVewd6YQ*aJ8d;(R@3t*UET9y|loMu(;R+#eL)dq#smQ z0#k>qfdq4}qHgC(vKD^L`~zH@%+g9lrHL`5tF~AGB!qAhH=CG+IvopK;g;uLxYH6S zT>OL|;G7bC^>?2Ub%q{}2hY>MXLHPTrlK^Bl~`;;Il_AWKD(cx#Pu^KnS7e3K>LDJ z+pE;Xxvt_}SFg_d=jwB*gU%e9`86&vmaq01+^afP_V3Wc5Z{)3BQzqu7T*aSG;Q`|_t>E-ncVo9h>nBozB7o?`A4t3-} zRXDkDzL+>jc0X~G*U&=AJ2^_EEOHHEHKc^mAil-&Gpg1ViLTb?Yi#+S7WvM&Y_k;f z9r37y0Z3m`0c4=!e_306O!Siu$j38P8~3E5JmjoP;GtUZkGszbRPIxBh*?8=QzAy@ zW4k^7eP=vi%#UO+5ySEOycSeg?0K{!we%Dd)(pK0{|l5@ zZ-F*dedC#OLf>+6l=x=+<_UHRQ=t9nlHJ@(n(&3RQ-pVrpwf7U5Vf?ldgKPbt<;HEuvs*i6AXV#;!L|e66u|C2va*lAg_K;-=&*oh1}|#Ppv0h9yAiY`&ek{ zx0?;T#XXF$O!2Zs8jqCKk)UW>I-x+DGbde3|>`3j(@75sYU?%^%H4KR)qHO_}h>vd**3k=i!s{Hgk97)F)tOS`kWp`pQ0 z+jl9`N#=c!<(;oK5G4G=g#LYHKQCLE{P-UO#Gos|D>QjQAV+R_dkBXxneby&Kgiqu zcK($p;a8ZkVeAe~ZoL`+K4F4${Af&rPE2xoyo9I0SX*aN4`uyjW4y7wUp_@)BiMg= z=C@&QrA0`#Q!G_GdpDEuT|3o~gp1-te9(=8wFFN0>JUK__q$?9^1t0U{!`RK1oGAM z=_Oy40jJ-e+X!(Fn3Sm~f0?A8*e|E(rebpX${tG1aD2$-Fv(YXvF9vHnV=K4crImY zz^Mq9XIT7l(PxL^w|N-@D0MBHH~P`PY}ixmZYTNRe~5UsBVAH^zW@a5(3>!HRF(zm z()K*P)`ZmvpCVq_?*%E=qw@5*-u-lCrOZvGE%u%b)i=WJGW%YCzq?NSAyu=v6^vB2 z;R*4pCm$EOvxK~lj2IHBFA<5nHwqI$qcxXJu-*wVy_o))NjPd9{^Zo(kMqA0zB@Du zf5NCKnWz4UDfu{)0C9!3n#jBW<$UA00<^BgQ0)Jm%gMfpU8J`)0!6-ax3lZtPU1e zs#Cq*J1Q@e9jAZOZ4X)G3QiI-2x}Pj0eSvvcjEesYh`RhlXfY{8Cux0jVWFKSuU#| zPqDaVS9wsN_qTH~X-wwG*ZH2U(T!ue>#d(vKGIb`jn3$Rvg1FkZ!vlMdf19M=j*skl>)itCml2|%%yVxuppZj&O9?e(IYgl(u$JXKz zc*x5E$}5w;w2)`^w~%zJSmuqtAm~%qYZCdH`q(*jEqv{%w`)s-XIB)%|BJG*r#|81 zXZtSWYa0X>Pu7~QKyhjuf!Eo!HeM*ZboE(zv)mIUd# zX_EY~w_a1H#|x`*aKKGCZx=tq8E?t4qYt%HUkUc{PhTndgEI=1zzZ@MDAh9mt6(eox33`MW4p-GI%UbS4PV0XOEJ~2yN!||aWRm^+@`c)? zCc)szfoO4qjMZq&|H`X7KvDblW(uqW$iB-tVA9xbX3wm*s`~nZ4Q8}8 z{zRWf52)gA) zJ2*2@ycvXtIu9g{`@gR z>-j}_x#9qC8-%_P8iD5c%z+<@76d+<=Qxu8HKn;d4$#tkNNBrqIX}o?pu5SPxgsVs zAXQQ}%6(Mm<^fSuO#Y_T$yn z&3Z@-Xeb8gKZd(U9+_(w-`E1)O@FLzY)dG=!j(9~kBLmId7~(GP*|Rb@MTyZkTPlC z@z_nIet&#<{bxRwxB0NMe1l2_AH{ok?#~d|!entCuDCI#MtRX|_Eq^bj@YZ^2ffRm zuF_L9PBb=0p1B1=3iSHPh6ebs_W(9>G;k1$sjR?au$%<_%~PTwQgtkBa~%PUUsg(*09pQc&l3LFU;iPL#JD+UgiPXAH*8W%C?ESP zs#CFE+s&}Sx`BN!C%)2zEcOQ9x_fqqMFc?2>F&7HGbF6soF!&?vVS*2ID6+f+u@Z6 za5bRh?7v~pzf0s8J7#({O*xJAq)Pdj@t>ETct-HXpUV?t)~#_Z2+i`EDORO`U;W*L zIBN~I<$sLA4W~I?MQ;mG*7DiJ3xq4TPl%t6&GArE#Vdc%lwlt^PzQ`IF){T>CD2H7wxKy^FxH$k3N4Z z8)<8;K0ec(vDEQ*EPvSKr}!&AHHLt zK9SyppcS?KAHG4W6R!8v^ySZBu=_0YFPw=1b9)&_*ewzax7k%w3+kQxc$S_>yr3!o zr|;)x1k^@M5g%}t>1MQ4w=hXYlNSwLNiD7M9~Np8meZ2SuLdFVKCyG{l+`I6b`~Na zLe_soOle}k8eU<<(mxncwZZb6hqp}g7lX!`l{WWMP1CJh#XRGN@x#3^_qx^WL-#yWj0@K0=-_YOa8X+g9K`66JEghdx7 zyGEE#jF}UKtz2}1_GM2QBt}6}+Ea{1SOF-=CwGnTaGruW4&d zQmV4plnT0)&pLlA4`U&IS#wj_jt9EHIw*u}CcR&}cPvwRq&yVG1pa4^<$s4ISHTj% zLagxkPa_878pp?!j?krjE&_;2qd{}OFp=~%kL2s)kVe`|DXwm6^SqGe=kbSu2aGPg z>YeO*q{MO5{^Z;Vb|t@-{ds2iz1QpXWY*oU4xX}tA`@-R$ZQ$>9FDCk0U~d?uK|DO&ldB zugHqA_Gqo=MoUul%3aKls zjbn+n4uiIyu3^(rCClkxIetVYpnMUCzCY`bB}Yt0dAfB}hVALUHj*O$8cE+;tE#g!twv-gR;8+3DKA=XZ9>In`82C-Fv0P#sh8S2dHP#Dm8yN3 z9p@M@{|EtH5U25{P)#Z0&@vf%yTBQ5(Wh#+)*3V#G}sah-2q{{JV<$b$<~$>;Tvv< zgfb(OVd16UdwC~XN^WYl?|gnRhcMUGCBDL}pNTVGqLTpfzd#vWiEJbpa+9d#BrNu@ zuY3nJ$de#1cJ#$He(ze*+Rz9@>Acl1M6yB^WM<{WZT(k9IbwgempUF^j6^CaCYyy5 zPmKmr`l7wrw=5@e6e6w}$EXx`8O`#bUKmp+=@(7?Pku(dPk&lPMJifw_0R6V&>Q|9 zG+s)8pdjr4V#7LYg8^N^-J~9V&eW=?t%+xg^Fo2yEU<1_*B}y+;$zQ@s$EZmj?Sje zLhw->SkYdC1B@QvebCg(9V`Z>DR+ZtYDbsiaA=%q2X>*!+24IZ_K~Q>1qe~waCJh5 zz)y)}kapm!1j#x&g({3{>vu;0+WReDVH)8FuT79t^=D`u<1D6O{QV70AMB#DWY(A< zY6cjMFz-&L7FS58*poE9XcW6tX%A{~qhBJ3EE( zvplT6P_;=9+ik=S%cl37e;yXOF(2j~a-sLcFD}38)GcCv)w_aU$QkOxcA%StSwJyg zXZs#auz8aTby_3meY)UT2a^VJ@}7n(P%U8Gz|CQCOjspw{~8}0d^y3(dv(!zI4%u0 zxv|Ju57mJ^f~yUD)gKa6%fAKFfiJ_Lq?BS`j;g$ymBw{&YEpE4_h_sG9lgkzXzBj1 zJ7p~rBD6F8UsAFavViRxYjIf=Y7O02MT&_Ck?Mb?8;9=Frb4p{(=9F7%z$VLX*-JG z7ZRP3O^0G6B$VLptX-aW^o_}O^x(;V-5~B!2aiwUbZt9bL>Eo>)?`DczGZBpj9DX<$LRpp z5c217#-ZA-QstC;k+j5&4X=og|3O-{3nGxg7zWjV6g+8JdMVDCNB@3GP5u7{#TJ2@7t?dfYaz9)0(QT1*| zbE<1peQXRxif8k?Ex4gW*R0>pod(~;-xaHRR?8oA|IStvG{6JEMBjM{9AP73bWkWR z9kEHsAkO$Sn=E4)M=YvGX}I zSawpd@PRz6p>#FwKeb)^*M8}@T6t5jJa2xTCt>zN!3txj4wZ|mkh{i~#v$uMJLVG9 z+itJByj8!!5#{zdea5)Ac(#jIyvB4v`NpV%*`kJJ&d9aca@vZV)K&b&AtLDH#}cou zTvjE{hfk9#UzJ)^kZ1`(xB`AAntKNP?>ZIYW@y;|_CMk$^5tGy?C(uVGL{hin*h)I zw<#vHp*PV{7~TkYa*qLglI=YUbUt;HGm&%h%MmET8NzJd#}%!>aN31_r{$Y!+0GM| z5xo`=&b0z0%zrXdI*eIbU}S{rKT2xt%>?h6PJ8h=QBZ|W7 zI8B)cl?Sp!u$~;E2|OmsMTP>ebAnNLsgGuhyU&g^y9EQj#2Aj3)Jy=#w@se!FFL=rc|9Yx7Y2l|8Ky4nZUmthTW?zyc`$ZX}ue6{k>ZU4hexA9gXh_aj z@Voi@$(Mbj+{vfFm70XUtJBjB246vBLbi z`EHg;H;yq*D%qt_^|>~>HGv7ycL*^9P`PZY`d5v;!Z5!|(ECEkh%BJvVni(Uo?>Y< zJHm)qsJ5qxxW1Ic^uy)Q3hDSDo|$&Jfpo?HUrxRe zuBTs*2VGY>6v!<-)`)rn^pJ=fvcT(RznU08U zC074|W@~R_Ax<_C7g~@%#hzR6P<+ZG@$*x<)wn}-9U@!9G1IGYVD>Dqi@#CNH_HLQ z`n4de-1AuR_r@3S)Nej0bbb>ntoToM!q0QTSep`MgRzyjRvRmA=9YydSd9;dUP&5i*W*3#@194GLp+VV7QRM3UkiRC~!PC!|) zeXpER=9K+#@uQCm=g~N24~j9FmtGZ}GDUV)rge*wcH?B@v110>`*O1v^urU3wOfCg2@AFR$E$g4%-Wawzw6Q&V0d0f37u_Pv`$1)qK@gu_E8S z*FltAuZVf8fVP`gXUlM#H~V`uaRhg3<+=@`D)Gnhoxz}`dRhZ-s(ODB3?4(6MsD_I zbfqypT+b+knZPZ~jK6{%&MJ8*tT&bgJ~48l@NV#Kr5xzEWpG7K_aSdEOf%PO#Wc?f zYySLnH|HNn%k`|~j`iOs&>0SHH`e>KubF=LRzxB@qO%~{KtZ4M-cNVXl-(&%=%8c@4B z@P2Jw%tKdedGH(@<}qu=A0O&Z&bD~AFP(|!_Lu@7laFiStxe!qMB0a#P~!gfHT|X8 z{(Ii&^(UUm+evu}&jTXw8G6#L8@UZ7e}*ymE~H8LlXac7v06t6?}pe+=JCf**X>V{ zHV;K;G`9H)o_{VoQkPXvmFk;%lz_h^o5aejxn62%l*bVjyin)C7Tm@6FMnvR97!>AZHTc!5pXo&kYnARJmvmW8&{>?XtqyL_^ z?{7i%BMC0w@!jZmq;Tn#Ng7xQMW%`)Z2}>8hM_QP?vH;=C+sz6^#YaZW!ED?&Jt{L zvLBxam>Dp8K30qfg#N;xp^EzT0D#H`fB$fY@=7yyB!6aVOnQ?P&~`E4PxgCh`I=@C zjdh-Or;s6>$Yc~#$7Ml2%sRP5j%H7=ouL`VWAM~i+o*{>?Av+sqJNGM;^|s1baupD zO+Bdd#8AL@B5BV17>)>GPtU<{n7V2dOhZi)onOm(QZ_fz#=)6)M)XTDl%ZM9@hgL2=dBV2?m;DuG3jLRJ_(a{nxfgBT-!j#UkeF>3D zkut0$s^F)p(guP|z2@lBsejZFCaAGATBenh^CQpw!)6Qezd6BMD&Q}_E8}%dd z7DsJg1Oac6ik)0oEhn3N|7r}A z%=uHJ+UJqUj1@pU7rV=ODr!xK1577W!um#YyXHxQuO6Ql!$B^)o|rfNj%grPhBm}i zy2O2<`81iCUB~hrvgw@ufTKkPp9W0uqV-eFv+*3Dr2A#_qdp1$wH*~}X2Q(y{@#AF zY?I)pmbOG$(FHeHs1b}P)0TGD8Db|zEno7R$7l`R6*tkT;GQ2(T z`1cPnhTGpPSY?|{b!2mDK4Mi7ENRc)*(bU|?xGg7Bp;T$p7IwHe0?<^J5;pNg9%m6v|LuflDHOkI=Bhu(PTlNbozW z!0AFgQ+a(cvLjiJQk5nDI2UP8F2X`WvMavO?THz$+MLfF<=ew=}Nh zF(n)!3tD!$Im*qSM5e!f7yMUPo!i~lRIy2P$S;>LlmsazypXS(I4|=`RA3K=d~CV1 zxA7Q0dAl|LtK)&JZGo|O&t$}m^FN$u3^RcR50@8Fvw{Egu;#RHT|S9JJ)tns>_3~r zeL{=#J^hR1VYu<%ttsa^!r97pMI0QPd4?7*I97;SdfStW$ zr^GNZ%@DP=&V0Tqq%>wKWmfHd!M`&>p9`b8>!eFWL)0zCPfp$82fWp?Uk z+bZX6Ex&@8_d+W_j7aNep_U*pwJn|e@k6QlfJAwsXKRqLw&E{{PMYvrzt;3;dZGUN zRF$axE81n*NL5ihmueG#D`v5JV*2c$XZP_f>(1l=T64lHuosN#su?F)?t^Jgw3OMu z-8w1Y@=hndQ62ZI80L@qWB052goDM8asUXNXUl5E$-g5M;jLNFky8Hiull|J&fnk2 zL-wPn8u(EIS_GJ4Q)jVkdBxue#=U?v+8#8b$`S^g{BPLz(u^w z@kD2@>(;?1@zwR{r(F?A%RRHJ;wqz59!?TEHCq_^%MUuRh0?5)Xn~z!1a`r~NQ5M5 z&;>~h?I`EGjg4FyXU=0VlLa}T?ac_CgEwBoixKTm0iW%)Ig*NQ!?D|5$LIYt5eVLKsf&#y4%Qzne;Dcue_7RC+Ib z+en{0jp{|2I)1o}%SNWFtfUyn9NV$BXS|xB$)zhF)fq^xD`{6&CF9>V3%T^F8L_|h z)@DX#BS&*V({$dG3GX9N4PoxARmq|ro|M-z9-u7&`Kkg>QoJ{A*ygyX-&#{^nA*Hm zrA8W*g(0d9ih_J@_dh6_vzY!G6pM8jgywv~jA7oSMDeTpZ(C#Bkb;RNsxrK5rezoR z8kOWgizbY5YFjyb?@%0<5Muv}AT@iJ+9mfpQLNz$;&AxSJ<{yRj03=2^BHTL7O3dE zY;Zb(;iZ;($x;?%9thz3XqS9?3lg z%L)++=6pPXCH~u4r_gpG3}Pj9V8eRPBXNMxiWR^#AV<4~%0BjofH8-Ju(#e5WE-a! z0imfy8;#>M)52Qy9tnJbPvU3A?Xh)btl8ni+4yD z8uV7(fH2Tfg=qbu8BD7&(@XyPb~aoTB}u7~kw#n#`kuQ7VI+_Gky|o_bT%AT+fK=PJhF4Mi&!b=*Cvfv)%}mgxN3vVRys`yWQA;5FX=nF@Mq z;c~T8-v~{4UUbdpEc#hKObp`>L_!ybd#1!)P*xP!i{jM-6 z*BSKB9OenXfy6G88)E>x#6-gBObTztp8E?sCer(D13;zU|XdVL`Zw2+Q zw%gB6pXVNT%|s>NJ%$DjKZ5On#>VLTQreB-m~|?e8D#EnP)K^($!_fH)0*LvhVK?r z6Q{0A5;e=y>_92rdwcvtKf{Dk;}rrj+{g^5$2kPL>f#or%1IhxYAG)xdw)2zB7O-A zY>!@a^9Z&ICDpbHt}AIjdHv$<(ANI1)`9>`ASIb54#h0k;PdW1a<;j@GV~IH_nMqF zxINwsx_V4R#f1w5aY}F&v00>%&+q(@Vk&QpaqZtVl+pVyIi54C&u^ghJl5F}So1r( z*CA=DCBLqiM>)yEYaovW|4Y`sUHH|)zPBrNlBi8&Y(#mR5>ss&Su`tMcOht%0>ic&+jrH+0UlFg%=Bv|aq) ze#QRlzg677L+#9O*q8!*KhiBB@x`!N$NKxr6J+IZRv-yvPx~aLPkzXoUbF7#gm~vjyprpY6y#z3s7=ZLfr_4CcmNyKM z8?~J}2x*U?(IQKSP_sZRGjELZdlTjS8*d^hVeTISAldKCL*Z@23hgNdBD##~?Yu`Z zx#YigcYiO2u}!!{NISe{&366Qd)~D{tfuvKAL%#k5rt;YM-uV;@6!H)_Ds`}A40A^ z9xJg}F|a7xCC^6i5zutBWko)J44tybg!Qwi7%7w1xeuh1=CQLEy%uB(5iOSyrQ`Hy z#5zaQYnicu@C_|c^vy$vfOA*KPb!5(C7Fr7Jid@IK58AouKyh1zV5W9d4`I?HSloJ zmH#X}41+Vnac0FW<^t6EU2HUM{jJ<@xgO(!%Bzan$Pu~~+;FpEXb1cV&Q`SpfJfY_ zqXx+8xt6m=G}5UpX}l?#i4$ud%PGud7$xV{!gr9_C2NSaWbuXH&)Fd-&kG-&`_FQ# z*fG&1Q}EJrLU3OVII>}MPegTD6@`2T;O5~>S{tiYRve+?%212<=i--@^}4<4k)T5| z&3|jB8~@H#51-NgMkXldiU;Mbnadmd%iem{YtnuWkNo9cG5|n$T~S6-M_Ce8kZwS= z)txLs<1vbjKBMZ-ckA1SC2pL0hGoP*&F|kYE5DUpQKp485Y92ffligqFKc-G8RqytlEChcUt8O)|E&OzPktS@wzk?8 zCX0MU&{Jvq7})p@j_veuRf;&;LmM2DecRfg=v4p^*)v`#_z;Sy$YYVz_+< z2A5a}pr{Zq=-c>91rd`#vAKv8C6}K_q9pMJlQumUK^gKXdt_ja0o+2&n%sFNtB>58 z)b_WlUnooqG`}nmpl4s&!I01*b#=W!?is@=Qg-yZ+pH&a+kbe)L{ml|#8aQ-|4(~g z71!p{^@|lLrD*Zug;Lz1xYGiKDlWy{-K{`@;Ki+Ii+k|k?iSpGrAQzUoD=qUzP#&Ct0o#c9Ov6eo^^DGYlU0o(o?=j8BGpt%=?TH1ss99Vy_VMWfC99W0 z*{n2fmuK1?DCe#-_?>QgEdLDjP1Vg?BO~yY8qQOGP^_^TbpVhG?B|ntcMH2LM6XSd z<$UWKYtn7|`WCXIDC;RE!rf#(_Kpy4KF((>Zg}k-%8|use!{}j4RMmGSK1(Oh-VNf z9%rI^r4dQTX@S#y#DHS19Q%81WNrTBV(^>ha*fS(ru=$k1WPZbF;1H%H>InUi9j9i zC6$7r7Wd!)Fx^?M7bY4h1HYB+vNZWH+lyZG;a<4?{qs*qNBpVP1Jw9r_skcUG8}bI z;VmFqFsB}ciK$n3-?8V$-vo$mn}CKD%v(W^7>MA>;vR)uxl;kzhn}#eay!?a@R|zg zRDH>;yAy`1Ze8~?cai}u?Vnm23E6g1A* z)U*lX-4~Gi8}udz`z;5`*C$nbYv|@v@FjzS`AFP_ueR5wCKZ#@bu<)r_4`wJY{EI% zRBtt}`$Hj^7Fy+#(;!R@uWJgo0{QAU&T&a8H8qK7!w`}h6e7}}KT;`VvP^MmdoRO! zlZGOV{_K=TojCG0UA+3nI&xSx+IP#(8}y|Ob#r(sJ-Y<_At9EyMyW?2>wRjxNGxAQ zq0)t%q05PC=xGj?-MiZ5z?$n?en6_w;)+Rk~daQ5rgvfD@kNFSTT@f(m%nT4uZ}Ejt>z158?)@C=qAh267BW4l3ZP1eT|A-$L_5$ z;j7-?ZWou99qSvm%}?br^Iw3wNad@A*Ehs(xI=sh2EK>$I+wnQ#4u@a9M6_uA&5yq zYta%ZYbeys<1mpmGT|nH_r`Aa=H2|N>Dery7?)$GWJ?xHbx}UXwolN|7Q}01i>ap6 zT4{U61NgI;@RZtL3puT8{BiY|hY-N<5PkUhU+1=qS6QYUdMt{u<(gn+$=wV1nMannLx z?0DkBvC*Z&)@8bHHL`r%wKKJe!gax|<3V)$Q+_&abQyV-%jkG){RG5}?`S5rk9c~7 zFd|-$1(>8mxNFD_?s#KarAVlx&QWQgj4^lVSp-m6_7 zfBTqQz!<~5McQ zUfVcDhVeDNMdrTpWY)DoZUI(Igt{WTZP39(2xmj@he?g|{hGJUs(9VFjJ=cLd@j6y zdOf3HR#toL8vKX$+NVm@x-BaEA~APPxKYcyFQ8-aU)>5ZTK=m34QT}vH}OwEVWXw5 z2CZk`X>(0iEQ3*G@8$NXD3y%IZu4C}q_c&(WDHF!-b-*8WT};89Q_K*GiWYd_9@D2 z{*)%*?gOWr^BsSTX6B)P!C~ZTQWL60`vWXYli~4+f$()I3M93q0k2S2K@P*iY%fah z^{ZjO>t6PT5Y`FuvmaAw<^n?wxY-N}EWEpaBJ(rC!8sa_EctCmTq0aECq(U&#tp5o z4>FY4!ctyVQ`h(?h;uAQR8jflxcJt?eP1gPe|dV{t4br5^i=#R)$*eMbgF&pG_hKP z7D`|JuxI6A?Y$vyzpT?9c{*&osbB(Af8{Y6|E23xIB>m|V(Dkqy{aEoKs>#~_3<24 zXamcRx}5lgkvOkBc3CU8llWrdhqq?EHCrplgCYXFA&BW3f+;*w(~d7Dg0BVy&f;au zr@U@)nJrY6Sbq<54fY=#+P+VoG6wrzy2*p*o=>3o#&>KYkL0;oF=Wm;8@zV-b7=`% z0u?qQ*THL$;nZ#*klI23AH1T2NSysvdrJveQ`Ivo^LW@W6@v+t5*dlVBG(dUjGV9X z;B;53sjHXBb}DV0na(94uhDg1r>&6rgr{k;8KP43KY3MM z@gJMW&5>7Qn~$zCgiE%+=I7RaL5Cept^Q%R6E1UkQ*_(Nscwgo_(OtUIF=dluJHC6 zQPeSDYn>)GC3q8!ogeXfr`Y|c(x>$hRfeKVk_&j=UA(dBfe>P?a zj;cq7p6gs~noC-gMYW+El3^4bVqN9ciG;(zD+C*sD=PgluVsl|h&kUbFyt1t`l&Zi zXWt!KF$o7v-absNK7(~u)$pLw@y>8?ycJ-T|mTzO_TeZgrRKm*p_4>YdU zAJ}g|SWrryBR68-weh>bS}oo7eYCZ-iSP`qGXlzWp}$4{9ZTJFz5E(03?(V9iv>JMc;rdLrz#j@))v%z z66kPvRWDZ=r*k2niEBwOZ~JjbPBsfX`1=or3Q^0x!fH{bqA&WS(rV7`^+@MnWyp3| zsN&R$Z{Os_Phv~M)vvZ86JEV-vKz)^bytw4XhGX=y%j_1e8re{zmVdy|CRgJK~W$l zkY7rlBH}gwrp;6Sc4qx2(sMAXaRhU*M+;gsG`79xCIpGZR;1M#$E2$WPC<)R^5{v0| z?yKFX6aimBFMWaMl^Oglhe{1n>8BU|u`at;{;&O1b zpN{Jhu4CY_3wzOBI85*^G_p2V_wgQ0@s8N*j3?r!e$KvU=Zeu}P5j?xE1K2OT^MrY z+-dMF^LoaMZz7svGkL2MPjD| zH5o#}m|7@I-Op#-@{K>AD=r0?t*B4~Z>i;B;m0%cbM$j?JKvKi5wFKrTD>^CFzAE{l{ z!jQ#ro^M2aN$we&<$wH#49nbF{%|zljHdjcg!0$F(j4=HNmY#~H7dsZcRF-M(mg5= z4z>pW!Kz*GVaCe#gUPdxNVn#n*)`-#j>p2@EAE#ex4$-g-#- z3RGNoQR821ke-t2gQt{EqSf;W@84+&iloEzkJcC3{>=s%6=M>)!)GzS|5c$?&cD){ zJ{UPVP+##cHaS*?=?@211zD>je*a5u zzbG5P4}*2`olzuA07UwjA}0DKeoS>-<^_y1!P1S7oAt*`A=(N9lL_r~u2j8SgG zU@*8=_L-Jyi_q~pzQ3^V8ak~-ya6miBY$9EfVu7S+R^>p(G+ATIlUCzvMpd~Ve#v` z{j-Zx$@^1&At7_E{-TVsrOVTusiV8YBkfFyiw^ukeBWK!FWEbi_m_L?VYKhEFTQ7| zNZg-FO5#0TSy40e@_KzcV{rTV#n@$$~5RQ9rT9wvR|>3`s%g=i*^adYmNH9Z6HoLH;2g*M+GsblKVo~TpDO_e`at5 zXT80KPpKIIzervtNY4Mf50<^DV-ZgK^MB57&wusG*?& z_EGZe952?+;e*Y*|*QE`k2nKz=^HsW*OoqAL^M5ej!_2KRLd@GCH=jhKvFwZeNDI_d7W zJ_jU+$Gt|E@x%!ytZi4o9BB?g6f7)$N;<`q%IjWHMx{6j=#Wc)#@S@NE zrf-V&Zk@L9p;_M9doM0dj@=B8<#SyGabXjkn!2h;-cCs_zbT3PcP|}z=+iat?iPz~ zw(ak>lU`rM++W1N58XlDLt?X!s}*>tp%a=cp5nk~N;gIRS4A$cjUH$fld7rviNx7Q z$-3_bO)jjr?f5rR#QtX`2ATYIS#3MsZ7jZ{J3jBc_LL3MD%JglfIiAsx2yNJa2?Q@ z&hwmW+tqVhOECE-#M1}8l3uv@6U7OZYdf#2usy*}oSM@lQ_B#Z7+8IU z=5e!MaktNm8@bQof5IXJTYhXo=xij;V$D4vE;;B!g}!+=FV;sr{~Y zj@P?pF#?(dVNCi>lowL&=ARi@66Ed84MqiY+sfnL`x|)l*orA-WJz@;I&HD_! z!5q;fh^^IXaM4M|Y-{)yUmQN3G^WfY={WnlZAbj*4P2;sdmeS{-9@@1Fb|Z@y7O*) zGO4YtP4xYA^bc6Mdn=o7pSzDD<@4vN3!hB+P=MZP(pp+sjYrpQN6I&_e%hueHTTYC zTwiAv_@|);#DkS?1s4Hlat)BS#P&jRomiqnf58Cu%wPhg50{KHA9N83Qfh_be)a0W z)sIC98ec>Cxq7CzYPx;`J+SxpCcbSMyB15|c)Ip$c@ERZ%OL3mcSs|`BF8DM5u;62 zS_i|!(-#W#D+%PU2BLN8R32y&aWSwLl+IuX+d{^V zxZ`Ixh3JU6=b=62`40Fz=3+87Ut@ky0Gc5RRlBZLQ@-hNVsvl5YL2@RO$Aj}#`+y0 z{Klfij5aAq&%b~FE~K>oae$e9FrO(Q-;YIjt)LR#(FuMpRQI%(?Mv%iF~h8$j4F~AUE_tuaSW0^ zSG7P|<#}prlG?E?>jRsjHbTP6xtH1B%S^Dj?Gqt3uGC`(`&|3ZLKkXcu4l6nQpo16EpV5>y_G1@bML=eF`0v!wM2c zUL!9|0>xFDEgw9^RcPFfx3ny+{DZNRSHo(%Ah-}%$DM>Pr(Hq&MYqr!blnVE6vY2} zi>oNvvW{nOQ5t9S(r7SdM6RIgu{I~y%TsK=0-D1}q(YaKOJ3zkF6@0gsnQJ-c)crv zlGQagWFE9pcCi^#EHkn(dqN}hXxufgHHIRFGZ-A5SZaM0#!*^GY{Y6c_P`K-( zrx96S!5%b1^cjJ=<;kwOZT#MrVhE^rQ>2}&KZ^bIQFNM*>Ng=zG!fM%6qST`Qr1Ul zVkY}aNL3)jTcLUwmsfwXL4W?9$5U0dACEq$U9m~-dEX35Z~CZF+R}QsP-jhr=7*Q* z7U!+%fcxPrp2k|L=9KgMp~Nr0Ow!2Luyl94y(+1k-K!R2=( zz<8&XW_C1gLD`ycFDnU`O)pKCL^w-=fBtOIkKA*|}_^$KHt zJ}3ovl`^IocDt}(|j5j z&4z6^Id{zv@*3&cuZNL4$s+u)Sfg%Tzl-&-GP~a;*sT3}WBT3)bpX~MPUx=PY#dX; zX;A2i*>TBU%$=^d)(&iZ9cT0N&r2(XK>%5_b(j`DxycMwaM7_9aZz78FQ)qX*KMJw zu-MJ-YA?1$HrZ2o0#^lB1nXD5k6Jlyy$>7B881CFZ9TTa?T59VrEo?%8y}NZ1#RgW zyI9NQ%X&7D$>kM$8j2)!xM%P!R_V?fUk>vdJ(*lj+Y}xgHhg~4Rf3mo45)p|V-!u% z-c>jF$&@-wG0DjR-)rML%fjnPBc|5~X)opQ=V88QOJ;uE?oO+{n{QiqL6_Pz8xwnH z^x{#r)6e!Mh$nRTHMPBTS*AsJiih{80JAmMhs_mLLGz}4!3fCtwILcz_8LqoZ&%i; zdk4i(H53lqzwGe-)cQ#)#m3ZJyw5OdskW+n=*RUqgBs(A?ii819%a_|sEuty8q=~a-F&obt&m=O4V-!M9h>J68iJYQUGi0UYt?wtO zcyzZf=RCgTO=X8^x)2-2z87c(S64jRgPV0x5K7Tgt7xk+S~okysm5K=TzY*@(-*l0 zEW1grl2Dkc5bxq~u&mICT$KwC-#){muYF8EU49Sm?s#f%$B$wsoSAZK=!#f&2TJ-` z^GMm9eS%BGV(QC80|?JYoXKU(p4H&+8Td?0Vpml~ z^lB@!P%@V++Jk(3eZo_pA#w`5AJe=&@wq(%gCRJm&5t|kA_7@Uuq`z!SZHb~`|er8WE z61`Ap=zWD*RA%5mC{Bk-SI-+NtY0dmUvYIhCJ8*v<&?PoQ}gW7kR3UeuoIRLM3z{D zmmh^qTZWX^S{KJ8ygTCF={Y+YV9Bn%FdlqMVSK#sI%cqY3`i5`1=>25U-*{q#)s<> z$6~ELgpvEXkw+MH9cV&`K5u8DwYn~@y}FC`{TzW)@25daGLUG(b8f-|=)v z>>5k&wS6{iDbu@Npe)c?3pYwba@MNJ}Wi z_eiYw2U`s&aZMREmO(28nfsfypGEs3oR_&I!8g@9Vuh+&$$6n1TRgYbZdHiRs@3G9 z9SAV7*R)Ku!aI6-`T+R3n-k~| zdVK@}-AZ&pTVyf{>BBw4#AQf3f<17!u3oj+97qt0)0Z4alu!A?ddN=4>qF3U5%cER zdOX6Bt1Q8m*jitoLpFRENWFS|j|oaT2?f`em%Zn9vi;j4EPre7_Nz(W)<$SyFL=_Q zH=K)LjRG(ULSJs_D_#<$HMW^`3&hh?u9w-k;&I&>`f*?$s#mP9+h2AB1-_l&t9*O} z*>Bv*TC`|Ma2wXIG#d~g)*tSrFyrUOah;!0DutY$LWq8WL?jII=RZG}Q(N9BtmP42 zFR5s=O4vKs#h$Dnr77z{i1E292{a{pj2L8wh5;MK@xA~$*d2pyOHz`)*?nnp+5aSV z%d~lRamTmUH_~v+Lu-2Dus|IMYqttEvvTKf7H+%TPRCNcp4@ScTcyAt;qfh_s-#tN zTJa$_MBWMf)xS$B&$kc0m|EnR&GV9_=o$xKeX-T8sJF}8fXE!#k&hL~t|w8+2JBgH zhqU|ZnumTVZqu0_zQfFzX+*{f%_?sg3+Il*1=Va7#f6i><6*Tiw z;vnx7MXXSur|x!|IcRqi16hMkQtkmFGB~p9z@DS*t5JUFQO4mH%&$D^5q6*dAbXrW z7!yG}sB}d^DGPS-;Qd` zZSn@wwF~D)pw#fRM2RW=EN%%;n(K9aZFtp!nNaX*eG8}Yt%M`dN;h+VIB{8STs2Wb zsE14`-Q60E{_iu(>6C_J_Uu;VfS}4>jV==b~zw16|Uh9jzy)hB|K&W_`_VExuu|mIwtL0cG9f$6l<}-KXk9i?*9htkZe!5SDQrPtTjWlSHnfiuNO*vuaYf zV{D1P?ZzCXo2Mu{nC8m3gnO&7Z)@5!!FaVGkYs6XKV1iVa9cVt6w7vi?;E$g`iWvw zm>5b!;0d>BS~@=lpS=}@tr&Sj2Vx3(>=1pp!N+XM@+&KB?_qYL$;10}@qFz!76Z!U zF|`rl&X1CXF2`Hbp|CWCWm7l5Gu$^281eQqsYsb99o;yX0+0q-SR91?NW z7*^k4!Ft*=S8|&@^tSbd&#z~X62ne=E^J}fpeMohE8t(Wqv@wo7Z6_}o^TCb02I}+ zzi#Y^m1=+5c!dwf%m9Z`&q|I`p{liV|6=a3DjbQwX=GW4h~ZDAg=SZ929C6KmUw4n z&6A=LlDAcTd+)In?oQ#%X$B_9vy==c^_8GL5!N1Jb!u3LDWvc>RWZC22idV(;+TW^ zWg@6_w4Z;=glalDcZ;U=0usKQ(a(a2;wHMpx)mswO=fBat*6LsRlvITQG%eECu7H) zd7?au^EIn=*5HLhm(SbjM!R1Y%@y^a+s>bV1Uie+yy+(E3_AM!q>?6& zD%JYwdi*64Rdib?FY@9?@8C;!#via!I}fzkik32HK>)bRcDe*Xb9s9v^86G>H`Rpt@4>s4PZZux$LUY=e^XG?{o#4cmh5uAm*ARb-6d zJ3@if+3*)I1=cuZD z(R@c^46^E{z(0DQhmG#rr%^c`4&Up!#A*dYTcakZl$zWAX zZY++*4e+(PYNU})(aTLGRUXAsqqF3Frp}>EGxWOt>*8qSq5Yi_+aty(t6=Bfx~YBT zkb1Fe<273JPovl+YkWp2)iveF@CGzP_94A4$dJ+ymJlp829QY8@z-8o9;%HD+9+1y zy&{w`xZ$7|%OC3fjoLcU_&GwYqDJWSVrt6htR15VIrwy+&rR$CF*_|qw zv0eUiQ=c^_B1lJi%tQ!O?uxYY^&`XP#9-0`S%!bNVof=Q!;&|65%ho-=7BJfl@ANP z9F8SSyoY4fdbfWPmS0yZ@rXV*_8QfHYa%(u_|z|uytuc~$zW~y(4!D1rvEfhBMIQ- z^i*hwAmRyo#QOvDZbvf}#taK?$EW1VgDMm}81&0Ct3Icy%^Ji>vcs<&^YaJHn;M<{ z=VCEsGE&}R_QHQY;#l{;>tKTxmz(R&dg>jS<4CR9XE(6Fi$@8npbR=D$QydO88??5 zH{LxC|6;(X2|Qcwa-|Zgrld_>++A65MUVQe=L9~_F(i1he7G3JY1!0fP4@bdb6E~p zP*qEV&(5P2c2ltSo91IvPSOUuu~nj=rTqvWt)T0&sqc26rm@KCz}K zNy=l%&`X-Pu{Vx>(A_A5exvF;ZdU#IerWItjv4>OYyLtPAgmQa9lBPK@#lJ#XE%6N zJU7~xAiA}R^P4d!^OPltVj|ofkF?Y{hep38>UZJrt>@V#yYEiJ3o}=P5JQz0aLQ5OMus13qSs$3Y!|=H^-ay zx1>Y}68o2xr1dXqPWXBvo!7;#06M2AtbvaczN5jYW27?iSj_rgw4sSWpx#FQs{10s z)-gr-dJ3;chv|5@@V(v2J^?Wm@Q|r~^YnXf@!iIg=|T=$X+7VKBE!bYo_@2_`K)2GR*fxY=*fC?pFk&$(6- zR%I>W9}6e2@WiO}xkQ~ogqiVT8q+Y~!ulg#%idqaVxeMz*gsCm84rsmypHftr>TUR zd=-Y@*@w6LD%co;Xz?G-_T_Jv^^65%>hlw6^ix}1V?H_vDD{ONOHav3owoVk*Dx$)U&C(~U}j&VnGC<_8X9YiUy!j>0H$+f6KmqD@9N zevO&Sr#^X())U1|`$%Srcp@eiO4Mw~N z9K)^j`84H)8PZo7_12F~7ODzX=qP_}rMA*|wsEU1?z|bf)y<*_ya&B~ceJE-6}5Tr zh-wF9-9FeGeed%V_xDP8BMc$*($1oeEne2y3wf=E^YgqkXgkiVM#YCiFju1`?-~9n z9SsB8-W~H@beN)evc$69>ix(_okR;^ME6%)K4ufsRw;@x*K}ZmgVEMOA=9Plu!M|%xb(^%JfUJ1%@F~h5OZtf?_ zicP>AE|J8#r1_--jZsX^WrtzZDTL>G;+JgyW*OsiIjSN4^A1fQQ~O+!QK+eSk}Ww@ ziRIDOs`dT3!Yq^^P&xT^mDe9N1Y2$r1xZ6;ctQ_xO0NjvNoU=+WIz@+r3&>>JR*6D zZs4S#9pbS;F-1Jq+%SMX#<+CpmCj{BMR|>vu;DVr#cwzUa z^Ku(wKo~L@A8}gFc)h@DIv|YR)9Q{4f4}2FUP8~im$uWUtlbhuQ(?tH#ghadcHp8P zemOtma%inD9#sMY$%GeZvt8@~eyDX2kh;xjRZyjlAXyM6MhK0{8 z$EyEigWuCw-v7W(&2gA?aPSj_%X{Lk5ceQX|Dd(0hM!Zny^Sy_h#2pj1fntOh5i$T zBi;|s%RSn51&yC}$UgZRjD>T=l||4*=DFGDS*VznYQVd3H{dy*viZ6_oV5@aC@~C^ z;vw!lgM!nA9>b!;(l%k8+azK>iZ?0q`Lb7*MC({E%E5E*yq4i5sZEsk!E-;~_tgct$BF%Rj`FoI8Gn=xpc{;q z>MrT;-oloYVK|ogz$r%o!lNYB73#KpumI59s>3qiiWR!@nlyUiHOdAK7hPv`haUgi zwwse7(vOW29z$4iG#SoMbtrTA^BcEM{esku#~(AKLdn#`AbrhBU>+Lv^A4z@r!W`B zi|f7;U8MOnQwt6lXgD>+%Me=e7zIXXM$>bgNK^CTv*Ynn_Hut?S!$4EP}mdMDC4u7 z{w%0M5v$N2EXWerLRtf#zLlSMk{XgPuzO9^z=Rgg+2nYUK0r`=bHGbOA|`g({NoAX z5S3K4fSAo?M8EM=N~(dlx!hB9S^_z82J{%ed+v7cenHBo;hY$RfX8yeXlTY(?42|o z1+@71a=4%vehgO6D!h*QoxruBEHun|j-FvcC#fYq00t#4tCPmx8(rmY5oflS_=x0;baE>ol;iq0hLsY=4QnV$n^GmbiJrn3ax6 zvC~5*9kTipz%?4DX@-Hsrgz7Mr8o3RS{tf8FU`GBV8RSbzl6x?7hYmPq)E1+m~7}0 z#9r$DM;R=A%sWkz5TnJq-NC~2j8JMY8qK@GZ-zx3FsJU$?P*O*bP?Ae~uFv+3$TYAJ_l&n0GLqS6i?DC3Mj-lzRdiuUY0=uz^ zW2%5XKhndP7DGv3QtC++0QAaYuD6O>c7dI*RR!I#Dbdc({QKIba1!~RgI)f2P1zP$ zV}#N9(9OrCs^}Ic$p*vAR^R#47~_|?{;hGerg`*o%f}R(~w20*QQviDaIHaNov zLA-~m5DBMAaXrR=gUwT^s$xToDrQJv*lc5aQcdbH|RD(OFI&YeqU` zSGO)WFR9glhvLqEy|SSPXN}cJoe({uvMAfn>o%f8r)8J)D1yl0)7i5!O*LgSqSye8 zZ5AmZ$*RomX5J4Os2r|uvoFWS`BDG@osRXCH7yTMFgfsIgulPJf>tbkQhu`bwSBdm z^C$sz^SeGJDpsE^r59fk@*z4@oIlI~eQ|p}D-KtT#)AR3z8mxfBT1g`*j4CH`UTg!Z^^fNgw3`G43V zbG=7;Pw-U_|Tbb^pMe??{+axKLgCH#k}&LINl*!Zxjc zz)>o;u2R&dRbAR%21m)?S!3G?ky9r$s3Wp}X~YARYG)-<7tS&&^B(<&9X1kcG3I!N z|D8`*9!dFF>N=+N7p8m2Of4<7?t#INANN1l5eepsl#rB&x^2_X^?DoULa{4G6)2qdLQ6Pi`?PfdaXI$_urGz@>Ueiw(NkXSD}|6df0 + AWS Lambda with Custom Layers +

+ +Components: +- Simple Step Functions state machine +- Local Step Functions endpoint +- Basic Pass state workflow + +--- + +## Project Structure +``` +├── stepfunctions-helloworld _# folder containing environment variables file for Hello World Step Functions_ +│ ├── img/stepfunctions-helloworld.png _# Architecture diagram_ +│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ +│ └── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS CLI v2 +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + +--- + +## Local Setup + +1. Navigate to project directory: +```sh +cd stepfunctions-helloworld +``` + +2. Start Step Functions locally: +```sh +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local +``` + + +3. Configure environment: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='eu-west-1' +``` + +--- + +## Testing Process + +### 1. Create State Machine +```sh +aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ + --definition "{ \ + \"Comment\": \"Hello World State Machine of the Amazon States Language using a Pass state\",\ + \"StartAt\": \"HelloWorld\", \ + \"States\": { \ + \"HelloWorld\": { \ + \"Type\": \"Pass\", \ + \"End\": true \ + } \ + }}" --name "HelloWorld" --role-arn "arn:aws:iam::012345678901:role/DummyRole" +``` + + +### 2. Start Execution +```sh +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn "arn:aws:states:eu-west-1:123456789012:stateMachine:HelloWorld" +``` + +### 3. Check Execution Status +```sh +aws stepfunctions describe-execution \ + --endpoint http://localhost:8083 \ + --execution-arn "" +``` + +Expected output: +```json +{ + "status": "SUCCEEDED", + "startDate": "2024-12-21T14:01:55.670000+00:00", + "stopDate": "2024-12-21T14:01:55.775000+00:00" +} +``` + +--- + +## Debug + +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` + +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) + +[Top](#contents) + diff --git a/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt new file mode 100755 index 00000000..65ccc920 --- /dev/null +++ b/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,3 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=eu-west-1 diff --git a/local-test-samples/stepfunctions-helloworld/img/stepfunctions-helloworld.png b/local-test-samples/stepfunctions-helloworld/img/stepfunctions-helloworld.png new file mode 100755 index 0000000000000000000000000000000000000000..6862146195227251f3dba4d610e8040572d82b7f GIT binary patch literal 53015 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 + AWS Step Functions with Lambda Integration +

+ +Components: +- Step Functions state machine for orchestration +- Two Node.js Lambda functions + - Sum function: adds three numbers + - Square function: squares a number +- Wait state for demonstration + +--- + +## Project Structure +``` +├── stepfunctions-lambda _# folder containing necessary code and template for Step Functions with Lambda integration_ +│ ├── img/stepfunctions-lambda.png _# Architecture diagram_ +│ ├── lambda_stepfunctions_src _# folder containing source code for Step Functions Lambda functions_ +│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ +``` + +--- + +## Prerequisites +- AWS SAM CLI +- Docker +- Node.js 22 + +--- + +## Local Setup + +1. Configure environment: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +2. Start Lambda emulator: +```sh +sam local start-lambda -p 3001 --docker-network host & +``` + +3. Start Step Functions local: +```sh +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local +``` + +--- + +## Testing Process + +### 1. Test Individual Lambda Functions +```sh +# Test Sum Lambda +aws lambda invoke \ + --function-name StepFunctionExampleSumLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload '{ "x": 6, "y": 4, "z": 9 }' \ + output.txt + +# Test Square Lambda +aws lambda invoke \ + --function-name StepFunctionExampleSquareLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload '{ "sum": 6 }' \ + output.txt +``` + +### 2. Create State Machine +```sh +aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ + --definition "{ \ + \"StartAt\": \"Lambda Sum State\", \ + \"States\": { \ + \"Lambda Sum State\": { \ + \"Next\": \"Wait State\", \ + \"Type\": \"Task\", \ + \"InputPath\": \"$\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda\",\ + \"Payload.$\": \"$\" \ + } \ + }, \ + \"Wait State\": { \ + \"Type\": \"Wait\", \ + \"Seconds\": 3, \ + \"Next\": \"Lambda Square State\" \ + }, \ + \"Lambda Square State\": { \ + \"End\": true, \ + \"Type\": \"Task\", \ + \"InputPath\": \"$.Payload\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda\",\ + \"Payload.$\": \"$\" \ + } \ + } \ + }, \ + \"TimeoutSeconds\": 300 \ + }" --name "StepFunctionsLambdaStateMachine" --role-arn "arn:aws:iam::123456789012:role/DummyRole" +``` + +### 3. Execute State Machine +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine [STATE-MACHINE-ARN] \ + --input '{"x": 2s "y": 8, "z": 7}' +``` + +--- + +## State Machine Workflow + +1. **Sum State**: Adds three input numbers +2. **Wait State**: Pauses for 3 seconds +3. **Square State**: Squares the sum result + +Example execution: +```json +Input: {"x": 2, "y": 8, "z": 7} +Sum Result: 17 +Final Result: 289 +``` + +--- + +## Debug + +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` + +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) +- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) + +[Top](#contents) + diff --git a/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt new file mode 100755 index 00000000..744075e4 --- /dev/null +++ b/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,4 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 +LAMBDA_ENDPOINT=http://127.0.0.1:3001 diff --git a/local-test-samples/stepfunctions-lambda/img/stepfunctions-lambda.png b/local-test-samples/stepfunctions-lambda/img/stepfunctions-lambda.png new file mode 100755 index 0000000000000000000000000000000000000000..984aba445f862d37ef8642952e7e664ac861adde GIT binary patch literal 56881 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%0Day90Y>&dH!@kKhPjyoS9bL5_)T>B)=>m7Z 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 literal 0 HcmV?d00001 diff --git a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js new file mode 100755 index 00000000..d0a537f5 --- /dev/null +++ b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js @@ -0,0 +1,11 @@ +exports.handler = async (event, context) => { + + const sum = event.sum; + + return { + 'Payload': { + 'result': sum * sum + } + } + +}; \ No newline at end of file diff --git a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js new file mode 100755 index 00000000..5d0dbeab --- /dev/null +++ b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js @@ -0,0 +1,15 @@ +exports.handler = async (event, context) => { + + const x = event.x; + const y = event.y; + const z = event.z; + + const sum = x + y + z; + + return { + 'Payload': { + 'result': sum + } + } + +}; \ No newline at end of file diff --git a/local-test-samples/stepfunctions-lambda/template.yaml b/local-test-samples/stepfunctions-lambda/template.yaml new file mode 100755 index 00000000..0b33ce8e --- /dev/null +++ b/local-test-samples/stepfunctions-lambda/template.yaml @@ -0,0 +1,71 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: AWS SAM Template for Step Functions Running Lambda functions + +Globals: + Function: + Timeout: 15 + MemorySize: 256 + +Resources: + # Lambda function summing input numbers + StepFunctionExampleSumLambda: + Type: AWS::Serverless::Function + Properties: + FunctionName: StepFunctionExampleSumLambda + CodeUri: lambda_stepfunctions_src/lambda_stepfunctions_sum_src + Handler: app.handler + Runtime: nodejs22.x + Environment: + Variables: + AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE + AWS_SECRET_ACCESS_KEY: DUMMYEXAMPLEKEY + REGION: us-east-1 + + # Lambda function calculating the square root of input numbers + StepFunctionExampleSquareLambda: + Type: AWS::Serverless::Function + Properties: + FunctionName: StepFunctionExampleSquareLambda + CodeUri: lambda_stepfunctions_src/lambda_stepfunctions_square_src + Handler: app.handler + Runtime: nodejs22.x + Environment: + Variables: + AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE + AWS_SECRET_ACCESS_KEY: DUMMYEXAMPLEKEY + REGION: us-east-1 + + # Step Functions Machine running lambda functions + StepFunctionsLambdaStateMachine: + Type: AWS::StepFunctions::StateMachine + Properties: + StateMachineName: StepFunctionsLambdaStateMachine + DefinitionString: !Sub + - |- + { + "StartAt": "Lambda Sum State", + "States": { + "Lambda Sum State": { + "Type": "Task", + "Resource": "${SumLambdaArn}", + "Next": "Wait State", + "InputPath": "$", + "ResultPath": "$.Payload" + }, + "Wait State": { + "Type": "Wait", + "Seconds": 3, + "Next": "Lambda Square State" + }, + "Lambda Square State": { + "Type": "Task", + "Resource": "${SquareLambdaArn}", + "InputPath": "$.Payload", + "ResultPath": "$.Payload", + "End": true + } + } + } + - SumLambdaArn: !GetAtt StepFunctionExampleSumLambda.Arn + SquareLambdaArn: !GetAtt StepFunctionExampleSquareLambda.Arn diff --git a/local-test-samples/stepfunctions-mock/MockConfigFile.json b/local-test-samples/stepfunctions-mock/MockConfigFile.json new file mode 100755 index 00000000..58d58aa1 --- /dev/null +++ b/local-test-samples/stepfunctions-mock/MockConfigFile.json @@ -0,0 +1,71 @@ +{ + "StateMachines":{ + "LambdaSQSIntegration":{ + "TestCases":{ + "HappyPath":{ + "LambdaState":"MockedLambdaSuccess", + "SQSState":"MockedSQSSuccess" + }, + "RetryPath":{ + "LambdaState":"MockedLambdaRetry", + "SQSState":"MockedSQSSuccess" + }, + "HybridPath":{ + "LambdaState":"MockedLambdaSuccess" + } + } + } + }, + "MockedResponses":{ + "MockedLambdaSuccess":{ + "0":{ + "Return":{ + "StatusCode":200, + "Payload":{ + "StatusCode":200, + "body":"Hello from Lambda!" + } + } + } + }, + "LambdaMockedResourceNotReady":{ + "0":{ + "Throw":{ + "Error":"Lambda.ResourceNotReadyException", + "Cause":"Lambda resource is not ready." + } + } + }, + "MockedSQSSuccess":{ + "0":{ + "Return":{ + "MD5OfMessageBody":"3bcb6e8e-7h85-4375-b0bc-1a59812c6e51", + "MessageId":"3bcb6e8e-8b51-4375-b0bc-1a59812c6e51" + } + } + }, + "MockedLambdaRetry":{ + "0":{ + "Throw":{ + "Error":"Lambda.ResourceNotReadyException", + "Cause":"Lambda resource is not ready." + } + }, + "1-2":{ + "Throw":{ + "Error":"Lambda.TimeoutException", + "Cause":"Lambda timed out." + } + }, + "3":{ + "Return":{ + "StatusCode":200, + "Payload":{ + "StatusCode":200, + "body":"Hello from Lambda!" + } + } + } + } + } +} diff --git a/local-test-samples/stepfunctions-mock/README.md b/local-test-samples/stepfunctions-mock/README.md new file mode 100755 index 00000000..6128e588 --- /dev/null +++ b/local-test-samples/stepfunctions-mock/README.md @@ -0,0 +1,172 @@ +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) +[![AWS: SQS](https://img.shields.io/badge/AWS-SQS-green)](https://img.shields.io/badge/AWS-SQS-green) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# Local: AWS Step Functions with Service Mocking + +## Introduction + +This project demonstrates how to test AWS Step Functions workflows locally using service mocks. It showcases different testing scenarios including happy path, retry path, and hybrid testing approaches. + +--- + +## Contents +- [Local: AWS Step Functions with Service Mocking](#local-aws-step-functions-with-service-mocking) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Test Scenarios](#test-scenarios) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Local Setup](#local-setup) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + + +## Architecture Overview +

+ AWS Step Functions with Service Mocking +

+ +Components: +- Step Functions state machine +- Lambda function integration +- SQS message publishing +- Mock service responses + +--- + +## Project Structure +``` +└── stepfunctions-mock _# folder containing necessary files for mocking Step Functions_ +│ ├── img/stepfunctions-mock.png _# Architecture diagram_ +│ ├── MockConfigFile.json _# json file defining state machine implementing MOCK Step Functions_ +│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for mocking Step Functions_ +│ ├── README.md _# instructions file_ +│ └── statemachine.json _# json configuration file containing step machines definition for Step Functions MOCK testing_ +``` + +--- + +## Test Scenarios + +### 1. Happy Path +- Mocks both Lambda and SQS services +- All operations succeed +- Tests basic workflow flow + +### 2. Retry Path +- Tests Lambda function retry logic +- Simulates Lambda failures followed by success +- Verifies retry policy effectiveness + +### 3. Hybrid Path +- Mocks Lambda service +- Uses actual SQS service +- Tests integration with real AWS services + +--- + +## Project Structure +``` +stepfunctions-mock/ +├── MockConfigFile.json # Mock service configurations +├── aws-stepfunctions-local-credentials.txt +└── README.md +``` + +--- + +## Prerequisites +- Docker +- AWS CLI v2 +- Basic understanding of Step Functions +- Mock configuration file + +--- + +## Local Setup + +1. Start Step Functions local with mocks: +```sh +docker run -d -p 8083:8083 \ + --mount type=bind,readonly,source=$(pwd)/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ + --env-file aws-stepfunctions-local-credentials.txt \ + amazon/aws-stepfunctions-local +``` + +2. Configure environment: +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +--- + +## Testing Workflows + +### Happy Path Testing +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HappyPath" +``` + +### Retry Path Testing +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#RetryPath" +``` + +### Hybrid Path Testing +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HybridPath" +``` + +Check execution results: +```sh +aws stepfunctions describe-execution \ + --endpoint http://localhost:8083 \ + --execution-arn "" +``` + +--- + +## Debug + +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` + +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +--- + +## Additional Resources +- [Step Functions Local Testing Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-test-sm-exec.html) +- [Service Mocking Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-mock-cfg-file.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) + +[Top](#contents) + diff --git a/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt new file mode 100755 index 00000000..65ccc920 --- /dev/null +++ b/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,3 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=eu-west-1 diff --git a/local-test-samples/stepfunctions-mock/img/stepfunctions-mock.png b/local-test-samples/stepfunctions-mock/img/stepfunctions-mock.png new file mode 100755 index 0000000000000000000000000000000000000000..59211be80ead88e6985e9da9bd63914a8c3c0164 GIT binary patch literal 47237 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 Rnm-lrGBL0`*P!PT_g^H-=urRw literal 0 HcmV?d00001 diff --git a/local-test-samples/stepfunctions-mock/statemachine.json b/local-test-samples/stepfunctions-mock/statemachine.json new file mode 100755 index 00000000..ee93935a --- /dev/null +++ b/local-test-samples/stepfunctions-mock/statemachine.json @@ -0,0 +1,34 @@ +{ + "Comment":"This state machine is called: LambdaSQSIntegration", + "StartAt":"LambdaState", + "States":{ + "LambdaState":{ + "Type":"Task", + "Resource":"arn:aws:states:::lambda:invoke", + "Parameters":{ + "Payload.$":"$", + "FunctionName":"HelloWorldFunction" + }, + "Retry":[ + { + "ErrorEquals":[ + "States.ALL" + ], + "IntervalSeconds":2, + "MaxAttempts":3, + "BackoffRate":2 + } + ], + "Next":"SQSState" + }, + "SQSState":{ + "Type":"Task", + "Resource":"arn:aws:states:::sqs:sendMessage", + "Parameters":{ + "QueueUrl":"https://sqs.us-east-1.amazonaws.com/123456789012/myQueue", + "MessageBody.$":"$" + }, + "End": true + } + } +} From 3d19ae19053bf94331659947f051c9ec827e1f5f Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 16 Apr 2025 10:56:28 +0000 Subject: [PATCH 2/7] Examples migrated to pytest testing - Step Functions --- README.md | 16 - .../apigateway-crud-lambda-dynamodb/README.md | 159 -------- .../events/lambda-create-event.json | 3 - .../events/lambda-delete-event.json | 3 - .../events/lambda-init-event.json | 5 - .../events/lambda-read-event.json | 3 - .../events/lambda-update-event.json | 3 - .../img/apigateway-crud-lambda-dynamodb.png | Bin 70913 -> 0 bytes .../lambda_crud_create_src/app.py | 22 -- .../lambda_crud_delete_src/app.py | 27 -- .../lambda_crud_init_src/app.py | 52 --- .../lambda_crud_read_src/app.py | 46 --- .../lambda_crud_update_src/app.py | 55 --- .../template.yaml | 131 ------ local-test-samples/apigateway-mock/README.md | 116 ------ .../apigateway-mock/img/apigateway-mock.png | Bin 59925 -> 0 bytes .../apigateway-mock/lambda_mock_src/app.js | 7 - .../apigateway-mock/template.yaml | 25 -- .../dynamodb-crud-cli/README.md | 132 ------- .../dynamodb-crud-cli/create-item.json | 4 - .../dynamodb-crud-cli/delete-item.json | 3 - .../img/dynamodb-crud-cli.png | Bin 56353 -> 0 bytes .../dynamodb-crud-cli/update-item.json | 4 - .../dynamodb-crud-lambda/README.md | 128 ------ .../events/lambda-create-event.json | 3 - .../events/lambda-delete-event.json | 3 - .../events/lambda-init-event.json | 5 - .../events/lambda-read-event.json | 3 - .../events/lambda-update-event.json | 3 - .../img/dynamodb-crud-lambda.png | Bin 49967 -> 0 bytes .../lambda_crud_create_src/app.py | 23 -- .../lambda_crud_delete_src/app.py | 24 -- .../lambda_crud_init_src/app.py | 48 --- .../lambda_crud_read_src/app.py | 33 -- .../lambda_crud_update_src/app.py | 47 --- .../dynamodb-crud-lambda/template.yaml | 89 ----- .../dynamodb-crud-stepfunctions/README.md | 143 ------- .../aws-stepfunctions-local-credentials.txt | 4 - .../img/dynamodb-crud-stepfunctions.png | Bin 47727 -> 0 bytes .../state-machine.json | 136 ------- .../lambda-sam-helloworld/README.md | 112 ------ .../events/lambda-helloworld-event.json | 5 - .../img/lambda-sam-helloworld.png | Bin 38055 -> 0 bytes .../lambda_helloworld_src/app.py | 7 - .../lambda-sam-helloworld/template.yaml | 18 - .../lambda-sam-layers/README.md | 120 ------ .../custom-lambda-layer/requirements.txt | 1 - .../events/lambda-layers-event.json | 5 - .../img/lambda-sam-layers.png | Bin 68083 -> 0 bytes .../lambda_layers_src/app.py | 9 - .../lambda-sam-layers/template.yaml | 32 -- .../stepfunctions-helloworld/README.md | 150 ------- .../aws-stepfunctions-local-credentials.txt | 3 - .../stepfunctions-lambda/README.md | 195 --------- .../aws-stepfunctions-local-credentials.txt | 4 - .../lambda_stepfunctions_square_src/app.js | 11 - .../lambda_stepfunctions_sum_src/app.js | 15 - .../stepfunctions-mock/README.md | 172 -------- .../aws-stepfunctions-local-credentials.txt | 3 - .../step-functions-local-helloworld/README.md | 194 +++++++++ .../img/stepfunctions-helloworld-states.png | Bin 0 -> 9614 bytes .../img/stepfunctions-helloworld.png | Bin .../statemachine/local_testing.asl.json | 10 + .../statemachine/test/valid_input.json | 6 + .../tests/requirements.txt | 4 + .../unit/src/test_step_functions_local.py | 167 ++++++++ .../step-functions-local-lambda/README.md | 362 +++++++++++++++++ .../img/stepfunctions-lambda-states.png | Bin 0 -> 24587 bytes .../img/stepfunctions-lambda.png | Bin .../lambda_stepfunctions_square_src/app.py | 10 + .../lambda_stepfunctions_sum_src/app.py | 13 + .../statemachine/local_testing.asl.json | 34 ++ .../test/valid_input_lambda_square.json | 3 + .../test/valid_input_lambda_sum.json | 5 + ...lid_input_stepfunctions_large_numbers.json | 5 + .../valid_input_stepfunctions_sum_square.json | 5 + .../template.yaml | 4 +- .../tests/requirements.txt | 8 + .../unit/src/test_step_functions_local.py | 374 ++++++++++++++++++ .../step-functions-local-mock/README.md | 251 ++++++++++++ .../img/stepfunctions-mock-states.png | Bin 0 -> 19643 bytes .../img/stepfunctions-mock.png | Bin .../statemachine/local_testing.asl.json | 0 .../statemachine/test}/MockConfigFile.json | 5 +- .../tests/requirements.txt | 4 + .../unit/src/test_step_functions_local.py | 231 +++++++++++ 86 files changed, 1691 insertions(+), 2374 deletions(-) delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/README.md delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_create_src/app.py delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_delete_src/app.py delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_init_src/app.py delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_read_src/app.py delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/lambda_crud_src/lambda_crud_update_src/app.py delete mode 100755 local-test-samples/apigateway-crud-lambda-dynamodb/template.yaml delete mode 100755 local-test-samples/apigateway-mock/README.md delete mode 100755 local-test-samples/apigateway-mock/img/apigateway-mock.png delete mode 100755 local-test-samples/apigateway-mock/lambda_mock_src/app.js delete mode 100755 local-test-samples/apigateway-mock/template.yaml delete mode 100755 local-test-samples/dynamodb-crud-cli/README.md delete mode 100755 local-test-samples/dynamodb-crud-cli/create-item.json delete mode 100755 local-test-samples/dynamodb-crud-cli/delete-item.json delete mode 100755 local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png delete mode 100755 local-test-samples/dynamodb-crud-cli/update-item.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/README.md delete mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json delete mode 100755 local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png delete mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_create_src/app.py delete mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_delete_src/app.py delete mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_init_src/app.py delete mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_read_src/app.py delete mode 100755 local-test-samples/dynamodb-crud-lambda/lambda_crud_src/lambda_crud_update_src/app.py delete mode 100755 local-test-samples/dynamodb-crud-lambda/template.yaml delete mode 100755 local-test-samples/dynamodb-crud-stepfunctions/README.md delete mode 100755 local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt delete mode 100755 local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png delete mode 100755 local-test-samples/dynamodb-crud-stepfunctions/state-machine.json delete mode 100755 local-test-samples/lambda-sam-helloworld/README.md delete mode 100755 local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json delete mode 100755 local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png delete mode 100755 local-test-samples/lambda-sam-helloworld/lambda_helloworld_src/app.py delete mode 100755 local-test-samples/lambda-sam-helloworld/template.yaml delete mode 100755 local-test-samples/lambda-sam-layers/README.md delete mode 100755 local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt delete mode 100755 local-test-samples/lambda-sam-layers/events/lambda-layers-event.json delete mode 100755 local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png delete mode 100755 local-test-samples/lambda-sam-layers/lambda_layers_src/app.py delete mode 100755 local-test-samples/lambda-sam-layers/template.yaml delete mode 100755 local-test-samples/stepfunctions-helloworld/README.md delete mode 100755 local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt delete mode 100755 local-test-samples/stepfunctions-lambda/README.md delete mode 100755 local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt delete mode 100755 local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js delete mode 100755 local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js delete mode 100755 local-test-samples/stepfunctions-mock/README.md delete mode 100755 local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt create mode 100644 python-test-samples/step-functions-local-helloworld/README.md create mode 100644 python-test-samples/step-functions-local-helloworld/img/stepfunctions-helloworld-states.png rename {local-test-samples/stepfunctions-helloworld => python-test-samples/step-functions-local-helloworld}/img/stepfunctions-helloworld.png (100%) mode change 100755 => 100644 create mode 100644 python-test-samples/step-functions-local-helloworld/statemachine/local_testing.asl.json create mode 100644 python-test-samples/step-functions-local-helloworld/statemachine/test/valid_input.json create mode 100644 python-test-samples/step-functions-local-helloworld/tests/requirements.txt create mode 100644 python-test-samples/step-functions-local-helloworld/tests/unit/src/test_step_functions_local.py create mode 100644 python-test-samples/step-functions-local-lambda/README.md create mode 100644 python-test-samples/step-functions-local-lambda/img/stepfunctions-lambda-states.png rename {local-test-samples/stepfunctions-lambda => python-test-samples/step-functions-local-lambda}/img/stepfunctions-lambda.png (100%) mode change 100755 => 100644 create mode 100644 python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.py create mode 100644 python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.py create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_square.json create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_sum.json create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_large_numbers.json create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_sum_square.json rename {local-test-samples/stepfunctions-lambda => python-test-samples/step-functions-local-lambda}/template.yaml (97%) create mode 100644 python-test-samples/step-functions-local-lambda/tests/requirements.txt create mode 100644 python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py create mode 100644 python-test-samples/step-functions-local-mock/README.md create mode 100644 python-test-samples/step-functions-local-mock/img/stepfunctions-mock-states.png rename {local-test-samples/stepfunctions-mock => python-test-samples/step-functions-local-mock}/img/stepfunctions-mock.png (100%) mode change 100755 => 100644 rename local-test-samples/stepfunctions-mock/statemachine.json => python-test-samples/step-functions-local-mock/statemachine/local_testing.asl.json (100%) mode change 100755 => 100644 rename {local-test-samples/stepfunctions-mock => python-test-samples/step-functions-local-mock/statemachine/test}/MockConfigFile.json (92%) mode change 100755 => 100644 create mode 100644 python-test-samples/step-functions-local-mock/tests/requirements.txt create mode 100644 python-test-samples/step-functions-local-mock/tests/unit/src/test_step_functions_local.py diff --git a/README.md b/README.md index 1cd775f6..36e0af2a 100755 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ If you'd like to jump right into code, you can begin with a starter project in y - [Java starter](./java-test-samples/apigw-lambda-list-s3-buckets) - [TypeScript starter](./typescript-test-samples/typescript-test-intro) - [.NET starter](./dotnet-test-samples/apigw-lambda-list-s3-buckets) -- [Local starter](./local-test-samples/apigateway-crud-lambda-dynamodb) # Language Directories The repository is divided into several language directories. If you would like to browse by language, you can navigate to the main page of each language directory: @@ -31,8 +30,6 @@ The repository is divided into several language directories. If you would like t - [Java main directory](./java-test-samples/) - [TypeScript main directory](./typescript-test-samples/) - [.NET main directory](./dotnet-test-samples/) -- [Local starter](./local-test-samples/) - # Workload Types This repository contains sample code for testing a variety of different types of workloads, including API's, Event-Driven Architectures, Service Orchestration, Data Processing, and AWS Partner Patterns. @@ -48,8 +45,6 @@ This repository contains sample code for testing a variety of different types of | [API Gateway, Lambda Authorizer, Lambda, DynamoDB](https://github.com/aws-samples/serverless-samples/tree/main/serverless-rest-api/javascript-http-sam) [External] | Node.js | | [API Gateway, Lambda, S3](./dotnet-test-samples/apigw-lambda-list-s3-buckets)|.NET| | [API Gateway, Lambda, DynamoDB](./dotnet-test-samples/apigw-lambda-ddb)|.NET| -| [Api Gateway, Lambda, DynamoDB](./local-test-samples/apigateway-crud-lambda-dynamodb)|Local| -| [Api Gateway, MOCK](./local-test-samples/apigateway-mock)|Local| | [SQS, Lambda, DynamoDB](./dotnet-test-samples/sqs-lambda)|.NET| | [API Gateway, Lambda, DynamoDB](./java-test-samples/apigw-lambda-ddb)|Java| | [AppSync, DynamoDB](./java-test-samples/java-appsync-sam)|Java| @@ -62,8 +57,6 @@ Event-driven architectures (EDA) are an architecture style that uses events and |[Schemas and Contracts](./typescript-test-samples/schema-and-contract-testing)|TypeScript|Event driven architectures decouple producers and consumers at the infrastructure layer, but these resources may still be coupled at the application layer by the event contract. Learn how to test for breaking changes in the contract.| |[S3, Lambda, DynamoDB](./dotnet-test-samples/async-lambda-dynamodb)|.NET|This example shows how to test async system by using DynamoDB during to store incoming asynchronous events during testing| |[S3, Lambda, SQS](./dotnet-test-samples/async-lambda-sqs)|.NET|An example to how to test asynchronous workflow by long polling the queue that resulting messages are sent to.| -|[Lambda, SAM, HelloWorld](./local-test-samples/lambda-sam-helloworld)|Local| Local Lambda HelloWorld implementation | -|[Lambda, SAM, Layers](./local-test-samples/lambda-sam-layers)|Local| Local Lambda emulator Layers implementation | ## Architectural patterns |Pattern|Services used|Language|Description| @@ -74,9 +67,6 @@ Event-driven architectures (EDA) are an architecture style that uses events and |System Under Test|Language|Description| |---|---|---| | [Step Functions](./java-test-samples/step-functions-local) [External] |Java|This project shows a technique for testing an AWS Step Functions workflow in a local desktop environment.| -| [Step Functions, HelloWorld](./local-test-samples/stepfunctions-helloworld) |Local| Local Hello World Step Functions implementation | -| [Step Functions, Lambda](./local-test-samples/stepfunctions-lambda) |Local| Local Lambda execution integrated with local Hello World Step Functions implementation | -| [Step Functions, MOCK](./local-test-samples/stepfunctions-mock) |Local| Mocking answers from local Hello World Step Functions implementation | ## Data Processing | System Under Test|Language|Description| @@ -84,12 +74,6 @@ Event-driven architectures (EDA) are an architecture style that uses events and |[Kinesis Data Stream, Lambda](./typescript-test-samples/kinesis-lambda-dynamodb)|TypeScript|This project shows a technique for testing a streaming data processing system.| |[Kinesis Data Stream, Lambda, DynamoDB](./dotnet-test-samples/kinesis-lambda-dynamodb)|.NET|This pattern creates an AWS Lambda function that consumes messages from an Amazon Kinesis Data Streams and dumps them to Amazon DynamoDB using SAM and .NET 6.| -## Database -| System Under Test|Language|Description| -| [DynamoDB, aws cli](./local-test-samples/dynamodb-crud-cli)|Local| DynamoDB CRUD operations using local docker image and aws cli | -| [DynamoDB, Lambda](./local-test-samples/dynamodb-crud-lambda)|Local| DynamoDB CRUD operations using docker image and local lambda local emulator | -| [DynamoDB, StepFunctions](./local-test-samples/dynamodb-crud-stepfunctions)|Local| DynamoDB CRUD operations using docker image and step functions local emulator | - ## AWS Partner Patterns | Partner |System Under Test|Language|Description| |---|---|---|---| diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/README.md b/local-test-samples/apigateway-crud-lambda-dynamodb/README.md deleted file mode 100755 index 9f7e9a77..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/README.md +++ /dev/null @@ -1,159 +0,0 @@ -[![python: 3.9](https://img.shields.io/badge/Python-3.9-green)](https://img.shields.io/badge/Python-3.9-green) -[![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) -[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-orange)](https://img.shields.io/badge/AWS-Lambda-orange) -[![AWS: API Gateway](https://img.shields.io/badge/AWS-API%20Gateway-blue)](https://img.shields.io/badge/AWS-API%20Gateway-blue) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# Local: Amazon Api Gateway, AWS Lambda, Amazon DynamoDB CRUD Operations - -## Introduction -This project demonstrates how to perform local testing of AWS serverless applications using SAM CLI and Docker. It implements a complete CRUD operation cycle through API Gateway, with Lambda functions processing the requests and DynamoDB storing the data. - ---- - -## Contents -- [Local: Amazon Api Gateway, AWS Lambda, Amazon DynamoDB CRUD Operations](#local-amazon-api-gateway-aws-lambda-amazon-dynamodb-crud-operations) - - [Introduction](#introduction) - - [Contents](#contents) - - [Project Structure](#project-structure) - - [Architecture Overview](#architecture-overview) - - [Local Setup Requirements](#local-setup-requirements) - - [Running the Tests](#running-the-tests) - - [API Endpoints](#api-endpoints) - - [Test Sequence](#test-sequence) - - [Additional Resources](#additional-resources) - ---- - -## Project Structure -``` -├── apigateway-crud-lambda-dynamodb _# folder containing necessary code and template for CRUD operations with API Gateway, Lambda Functions and DynamoDB_ -│ ├── events _# folder containing json files for API Gateway CRUD input events_ -│ ├── img/apigateway-crud-lambda-dynamodb.png _# Architecture diagram_ -│ ├── lambda_crud_src _# folder containing code for different CRUD Lambda functions_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ - -``` - -[Top](#contents) - ---- - -## Architecture Overview - -

- API Gateway - CRUD + Lambda + DynamoDB -

- -The application consists of: -- API Gateway (emulated locally by SAM) -- Lambda functions (Python 3.9) (emulated locally by SAM) -- DynamoDB table (running locally via Docker) - -[Top](#contents) - ---- - -## Local Setup Requirements -- Docker (SAM emulation) -- AWS SAM CLI -- Python 3.9 -- curl (for testing) - -[Top](#contents) - ---- - -## Running the Tests - -1. Start DynamoDB locally: -```sh -docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local -``` - -2. Start the API Gateway emulator: -```sh -sam local start-api --docker-network host & -``` - -3. Initialize the DynamoDB table (though Api Gateway -> Lambda crud init function): -```sh -curl -X GET http://127.0.0.1:3000/init -``` - -[Top](#contents) - ---- - -## API Endpoints - -| Endpoint | Method | Description | -|----------|--------|-------------| -| /init | GET | Creates DynamoDB CRUDLocalTable table | -| /create | POST | Creates new item | -| /read | GET | Retrieves an item | -| /update | POST | Updates existing item | -| /delete | GET | Deletes an item | - -[Top](#contents) - ---- - -## Test Sequence - -1. Create initial item: -```sh -curl -X POST http://127.0.0.1:3000/create \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123", "name": "Batman"}' -``` - -2. Read item: -```sh -curl -X GET http://127.0.0.1:3000/read \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123"}' -``` - -3. Update initial item: -```sh -curl -X POST http://127.0.0.1:3000/update \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123", "name": "Robin"}' -``` - -4. Check updated item: -```sh -curl -X GET http://127.0.0.1:3000/read \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123"}' -``` - -5. Delete item: -```sh -curl -X GET http://127.0.0.1:3000/delete \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123"}' -``` - -6. Checking item does not exist: -```sh -curl -X GET http://127.0.0.1:3000/read \ - -H 'Content-Type: application/json' \ - -d '{"Id": "123"}' -``` - -[Top](#contents) - ---- - -## Additional Resources -- [API Gateway Docs SAM] [AWS Serverless Application Model - Developer Guide - Locally run API Gateway with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-start-api.html) -- [Lambda Docs SAM] [AWS Serverless Application Model - Developer Guide - Locally invoke Lambda functions with AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) -- [Amazon DynamoDB Developer Guide] [Amazon DynamoDB - Developer Guide - Deploying DynamoDB locally on your computer](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html) - - -[Top](#contents) - ---- \ No newline at end of file diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json deleted file mode 100755 index 1c47303f..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-create-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "body": "{\"Id\": \"123\", \"name\": \"Batman\"}" -} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json deleted file mode 100755 index 12090c66..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-delete-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Id": "123" -} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json deleted file mode 100755 index 4ccb6ff4..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-init-event.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key1": "value1", - "key2": "value2", - "key3": "value3" -} \ No newline at end of file diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json deleted file mode 100755 index 12090c66..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-read-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Id": "123" -} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json b/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json deleted file mode 100755 index 074569d3..00000000 --- a/local-test-samples/apigateway-crud-lambda-dynamodb/events/lambda-update-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "body": "{\"Id\": \"123\", \"name\": \"Robin\"}" -} diff --git a/local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png b/local-test-samples/apigateway-crud-lambda-dynamodb/img/apigateway-crud-lambda-dynamodb.png deleted file mode 100755 index 91cd84de657d7f3295e95f7987ca7563c2dd7368..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70913 zcmeFZcQ{<#+Bl3z5IuSwU7`&Vok2(ty+`jvXY}3@g6M*%(W6B-I*Bep^fFrXI--v5 z+s--9d*183&hO9fuV=1n>}~H=?sfOF)`Y7l$>2Vue29XAf-5I0t%ibfp9uv8jSlkx z@FZQwY83?qN6A`BN<~geicSUUXlZR{fr26%{!ts_wR#_ErkvqK29OPmTQ1xlP6DUB#9!edq?FVRtW73pHF&^}LNd$|X36K!!D*R<=f z#mmy4giQ&69UC#1-Vwl`Qwg$4JoINzdA%1o1|qgkx)6YbaU&)zS*8fDC-n?ObBV$MPJA+fZ0B1JTI@}*4W zTZqs-j`ysOq06TszNNAR=}!tUuDNi3a3UiwB!Ac7LwNcazEF2?RY zinGJ}2!Sv`LsbHSXLWQSE~Q$CA;*uHXzhz|ZFB7JIpf!h7!v415RX@iG5&#f-XBVghu`)H!h=jb_1h)N$%!SdM=`a! z=ow=gkGUKU!-Gds;oqc1W(km8E&G_B$6@35*B3LToP8@+60W{Rr(kWOPk`R7gz; z?J&8c+ICZ-P>DqInU+ndlF%ADD?iYh{bg5q?|MZdBhGg1M}nq>+Hd{s+1*RkxnywW z$MTsE&6`!zP&rF2NE&ty8|W7J<82^P~rP(89xxlu{mNxPTPW`hL& zU`W#8NC$bw;uJjJt6mgN4*6uv^%^}w^2uvl{=kP7xI5_Mi#|>S&jKx-9;M$;ZYOcN zH~X%nkVq7xWs&bRkPZ_on^Bn0Y>Nj-*m0A1doC`8Tw=?e0eQBpG*Eq%1za5jFKi2s=`GX-JPrcOTqPl?t3mt z?VQy&B#roGGRis0zo_0|oKx?1pcDjEazFh^xNI!S@lgI7FSigsi2YlC1zRbeZSc?; z$F>5f(wv18szWM!sz%mt-lW)i+H}5=eMV}N zXiNP|uzX)-nMN_pQh9k;&HB~Ms~OrEtQndacHs;MMPy;&uft9DO}-=bBa9pOMcBbmH#fn&DVcI`D~_O+D51s6oE@y%9f* zA#-E;mU5b^i*kTx#(K6e$qn!9akGgT;YmhdqMz3bPo~o4}4XokE{# zmTHNVmvu$@G5s@9SH%s^Rvx3?^54i%ueSr(A~Z_aG1%pp0mLRC`j|JV@0D=U>K&F^ zWJ_eN&83LnFUgceHiGPl#2C*D$xRhp6)b*^PB~Y)m<@IDEEja`M)}Zy2-?`L9fIwH zd9#`0;!;vyC!Df8;kLF{>&dH3SW|3Fv`gWTb5NkqXDZlH+DxN<=GCz>r~w;W8cS2R zEBKiTH7Wi*Hd`c9$Wwq-K=X<*k2!x=@#69OM;ch?BWIgwqdxCMf4}_g64&;o<%`}9 zqanM0((i;y{#^D^Ru=8E-Fd>I z=w~wC-i>`nI-_t4yedK8!$Hej|-1Mq^G1Bq|F(h z1P_04?COl@lvVFqfwQhkx$FNl37t~^PETsEbRiq?6O0jY?C~}CKfg@6Pl}zyv7t|1 zRpCwOraG9D6`3A6L3&jyXze`uXqg#1S@fD^C2huZz|?EMEPq}e#@Uf9msHCMYV#dE z7-G(ekIWy;x5x)+V`}SPy3fK^7f#B5zMih@_Zy!W?;IZ)7nnRPD>5u;sdL(0b|ab` zFSjZO>)cF+T2pZ)HYC}@W#_)kPN~wW&e!)rN4FF~>{VB3)(W901 zb^lAzv~7)tCZ}#m>F#`T<6b{9XhC2?XkvG2XvWAJg<6I7bT_O9}uDDAy6Jv1P# zA=1HPuCL|rAUW6wGHc)&<5a%VI&oEn@e;q-zxKi{Ak3#{I4lTX57Z^rc=7u5^o*!0 zAtpmUb0jNC?5UsD=<3M$EX}OnDy*-~4U#N&^Gp4*>0~d>XsY#eAOA*N*N64ky*06I z=f<(+c`1AT`nnIHKDz#A2zh>Z zeqn*uV7#b}a<4d7@c~Kz0yQub2Zbn;swiQ6y(1|2%TUcd%uCi%h!h^N2g=r>xSs<4 zWmCib_8@ri*C4!yI}5&xPc#=Z6HSk6DeBU(!ya zeJo}tpT5TSvkQ6NC9lX01Q-z(ujMQi6;arLdrTB`R7#Zlz#S^^g`iUX&;3i($0+yy z@f-~WCCnNH{a-ib&uh#dOKHvN69*quoha#yi zB_{`5)y<$577i|N99^5Qm5G1{SWdD!E+{CZ%(q`uIW?v~!1$Ba8n0bnD=G+^IoflX zm^+$UaCzE0-HwAI>M0D|+FQ7q(0SV1Ik*UWiZT47g)ne``xwkX_m3v7wqgvg6;JI!T#_-0~)kzo(_VDoF@_5SS2(<$92nh*+ zxp~37yqrJ_P8Tl+R})W82N%YF4)WJH(iSdeP-`bwYexsV+i^`y9o<~T7#MCR`k&7~ z;k59y{`X7{F8^8^dIg2 z&zpbG_`iC-{&!EFr#$@syX*gY^`BieT`Ztdj`qNuu8@Dz>tCJ!_sf5E6b0Wd{ePk2 zpLqVqQvlJBhoa#BAsXai2ahUy^us*{_3nvHd(Rtb%`N9_o1wcs)k1Jt5`Z& zHOUWOwu_tAZc?>*K1dr#nmmX#4h#xc*trqXbSvJPM;a=7C?nHa%iCBF9p0%ID*25Y zJUy;PwD?^eIQ!C}pkhj(pb-S3p#R6MYmfve*qrR~=>7kE^H=*tEIQePkpDhF6iTr9 zq{=Aaf9oGe*EjTkru}R1Uwmko@di?gGIxOX&o;qK;`jdBONmKLI$3vIJ{rk8*9HuL z8GniM*IfT+2?mUTbklreuIT>_Il#i7{-0id0pt4j!vBMp{z5c2L$JA#5r~QYzu+#2 zmigDhg19N8R4g@5qG0gd+#%ukh`c&@1#ux-E)eb*s4C_;bko2bbM1ETNFn(zBK$Sw zzh?UKNFuiMV55&Z*j!nOJ><@jqnH4y_gYiw{z}fg@%LztG~%v#`C9634l+i^99y6W zp%X$=>1o79@2iAJbz>-v3zQzeLzI4w#S3BH8TEGkK;-^MWMU7$-C`vD95ir*4i_afGAqmz*Tt_e3#fuY2>!=-4@f>%_+ zP4MsHDLQU70Ct%O^N>qAS^hUwm?U>ictnnh-bb7E>l+S+B$PX`BIr(WBF6)pR1=YUCVPGn8Gt>FX|FHcH9&OGThMmYPft~HRD6yqoD~s z$KUR={vxnrExmf{(SinM`J~1$GRPL~`JvIA(am{oA3mnkNZ!$hKog!EnXMb3dP$B^QROTw$2X0vUr2l)<+ z`?*W`qic`CY(BFy7u-i)_hq=wG|hGND(qYerCGH;~GQfG=ER`%GR8y2OZI*TWr}>}Rzh9#L@LYDH;c z{cR=xi%$%6*X7E37kyB+gG3nhDxT0rEp!Q*1HRtBx&BUbwdR2?IgGQ~h&1$R-2U3y zJCFhy$olNN&yMFBa0btH7}cR_Ivny!_8F|s`prVhqni$S8Ls!@C+O;k4ABW|nJm+- zZTus8$`rh^sAGb4H+`zm6(XOO7X;||`+fCW;wbU&I5I~?v<@AHZe|-f49|D_ou@|5 z{rvQF&e5T9V9s~jan284bcGabxWhm5rkIS)=pbjkuc(XFvl6=6uZiF+Y=#$o5!6wF zWL{;e&#@c%2Z|Scv0%+~FVN#M&G#GYiD^A#CM2;sJJI#O3Bhl;cfD#XWZlsJ^YVBV zqxn}x0K(^Lv*!#O;Q|iWH8DgN`(58LHyj3Hg83@D$3V_gE-sT>*Bn}RJ)rVPMu)Aj zA_usAox9HtWKs+%G~|R2Inwrs$IQiyC#f;N+x${Wv>!pR-O!n&T~@o_(`~NsZxtcB z7zB&S&)$nhsyg~jkacayw6Q9tZ)CB1UGXK??@FJJ!t=^6j&Kpd^5xDYE7D7Zj=mW_ zaJ`trLiS`1gDu0OA&f>DklqfwB!shWYV2yI$%s@uff>H{qOT|KI)!K+;m~;O@lC0f zwb$iwdHseyGI(7~Ub@#Gc?G&E7umG8>OXwKT3_yeO$i1d@!lkvY*aS9dErI6N(l~* zklJ&}>1-Gtd!nRlZgXRwW%0>-JALu3xfTD|-RAEDSCBKkEZcK&6`_jh&E7R)A*{2| z$*V|_t1*}-+~{&k08tOi{GG&Quy!(K|Menf$Y8fX>C@7S_Kp~luDR2av{Cy7A8{{b zWWKV|rqvd{$AE{6`|P(i^mWE$!(GVPCODKLFGaxt1!RLQ`0#rkEq%-4NXabg*QRe}yGGmgH~aSk z_8{KBbqqg?p3zt}K3#AthAYf3UQJV7kG9-|Bs1|GH7woa;AcH?oXXt1zMO1pNNzr$ z14}3+tt(wmCVzEn>V3*}%)e9adzFT0j{NE>FO5Uf$5_&hA~_=K^zKf}u>q)iX4x&{ z;}bhSu9X$Jgl3f~Ft}KU6I?Z%JT2JeX_(>j%6`6S0y( zHZ9zIYdK$tiFTRUixxjH_E<`bj`z>FD0lQ9%5d)%f`nrE5lMH7_O+ahm;dnt^g#dB z(Ry3_=tqJ2ru7B(p+7# zq9HrD4r?E*C1VrQ>XW(j5LsZabBV>($kcm|oEK7UI0{c@bBR1uidBB8xr)+>& z2)z2kh&{)Cz3YS&XxNhL1^FSxXJ0}^kXDYT5f{JSfn4^)u9%RH<^Bz1PqS{~C+kT{ zCN|8T%rUQLZC-D7fuSWY^hIx$*yr4`&Y<4dNG=x-*GU7fIJo6Dr=R{zQAB=B!{ss}TKp$+s8zoybMN8v*dz7+yM4Cy2Xx$c=yG0tAJP^Lk*UcI%hBGsa9wn9 zF=ieAqq;Zar$@zw^vCLCOagFMB4mf7pWJ-ESXWko?{OPw_5Syk%=)H_Dp7H0@TutK zRKnblzgL1!TyxEuS+eghu~y%AK6}&aKdgIi_Jrqsg)F5`B6s+7i?#!D-Xp7}`Eij2WkPH7OJ%3xtI4)A1CK7gtR#4sh#2l@6V)GV zNvMRV#`8O!@ed3^^JGjf>H&*@bRkPWq=lVXrg=@aMK6L(K_Au*$BS0R>H|alUa5`KvCt2?IG5x)7Nr28^37+W4 z)%9T0tfBJEfZtYe;vvn1^W zuj@Y8u|n*f_{=2CiT74N@gPQZiEq|~gYpxtw7$Ns?DsovYug<>|CN7@yV!9y4LGcL zd^7(=si6mD0>nra9`{?to~&;W=}RkL?fh+UNY*RYt^VSJ?^qV^SV*Y%OXr;Q50@8K z;&TSh`weFl@m-v7B596Ui^APG?Y2`nFy-|n+2O1+Ys+Yc(~b$Ew*&8hl|1V#So?fk zY%rCt!@y%)23Vcw4tuk`yGq2WG-!h~5+kYvusp$uosGf8m=T*@lIN+EH`s&~j?D;v>7T`C3 z>8uF5Q~QxWK@$VZLR@YNg-I{~4jARsZSJHwT!S?&%$~zZ{k=<$hQnIsGdz7gu}=W8 zU%cMM?KvYGO{x6eh$zOMeR{GmS*Fv{-LifUJ$*_1UWIMi~s;?ZCcQ%#ssC-j$zPK`1xE51yy`A-&?84st>6RLWVu_kJ5bhPmDx+TuCm|8Qa1&8>(Q z-!k^N91a8AcGjk>>z!nT(9q06R`cvl1K7=WvUgzNHw~Y|Nu6H9p5N8swFUpA{)LXm zDV5P43!uTmw_6vv>W|o5Hp{(5PCsmpUQ*R+mmBUHRj5;bk@urr?coyc5j)&eKGe** zz6_{8bU~T6*u(P$_@Ay3yTH3F+zx6b% z{z!VES~b*i`YvD{;yOVAD0hm34?Tf83wD0ri}u`a_QJ#u9UJ*iM++5?9pUpD^Nlo+ zD@0J(^^T!a`!*95mHaEC&n_Lu+Vw&rgD1NrHS>d4#rI;I0@4EhF}`Q2Nva!DbD)6ZxI!`*3Tp!NJ3ErHSSJY zePFv)0&amapp!ks+hsft^#Y~1b@11ZWJ4UzNbx_h7)`5dX$=GX@J{jM{V?&fPZt+f zVsitr$$cB^$%hW^JL|V{i0n^p z>D<(VMTUIP-KF*203#A61HSpKZZE`XYR}h(c?19B3`7@N?0gd)NDC z2*iCP;E>$(mFd@3_3KDWv|L>P!ZM@?f5QlSGyKS78NA-mLkcdNkyCotO(u3TeQ?=B z;qb1xbL~R>ykR!Fr3M;5LTS&z4=CJs7s=YqV`3&T2CFCg*4IKGZ6`~eeHgBpkUx9?^ zt;3#NtR@!Zhwia zSrEu@S?Ol44^jT(0r(YzZ1kcd!PmP(wovKMGWYvx5$%3S4U>qceSXNnT7)f3(Fvb3 zV`<`?Z1{tp3H0D?ErJHDi^vr5h}UBV&*?Opmg#W>9Fgu?CtS?#D6%wuVo)jWZuETT zBI9pTme%H!E18AC@7^ftlOsBxi>}tH|7gqFsA+pHornYYkNijBP-$r7X33mI zxid6lk=AfxFSu>*`IU8@?SPd;dh9xlzI0>S2{kVzMK}@w%Y(1%T%R~HfnG#v; z!%}ZFC-RNch_A0KX5Vv8`7h&8Sr3;GJW)#(qLOFy3 zEpYdViyX*@l|iemlw6Hk{Gyr`Wz<)REkuun-WwAzVW46W-7S==^uG>+%|B1EChV5bd(q5mU-hW{~pCfbB+5Ot!%m~}{+#qTieAlW67MAf9=dbQvf5(H-CPDrn67@%W)Rl?pT8ywD34F+;$d`2KFdl z7;3QE5$sYtbR3)Tl+wqh)mfguDD(H2TqtO~ZVgB5t#NsPyr-p+u>32;7qF63fPt`= z>~A1Ji$(U|Ir(e#rrAT8Ek+Od@-_i%C!}Ad0E6?Ub;o9cR@0To>)+8gO;`?t1w?bw zg!l8O7QU7(t-K;+0U7Pi=Ki>tGfEQlxXJPNxCpVGEyF(wKV@}nHDrV-{y1cHb3I?P zm9nfnu?^Sg_o-<4GR121Wj<6)HS>N$-`KdrB&T*$A%gqcZe5o&Y+PZzE`5I8vRA~q zKw(5ZhCZn_ClOLtwP0|az=!BLWNqrvdaGA^>=kd-<_ehZjKX@C8>^2OCp4o>N#6jd zfGAUDMFxxYZi(%nGp0=Cqd~dB;|$RP?+k^atEQ0@$#b*sH7WIbwPwG)#= z|3>#9Rj{Z|7!9oUb<=}gI!~X=amTO0~p*JNbOTw=mN#M2j9Z3JR?YS(pxCCEB zru&caMkyG6TL$((jTi)|WL;!5|6KdvsN@_Ip+b?PmG;dKVErB}_Pbd>j+I;Kejg4r zc(%J`KheKI^V}I(+``&uAmHJU5XJKTHTm1`cGIc64dY6(TblP@Uggygdk*su!+cBT zC<&>_#OC)9el-hz+c#PbpoNw=AJ6H|Kh!^#D3XYnCngas-&=OV2Qw~fu@Hn_>Ym;c z7Ln_N_c7x$rd96Ut@9hvyIbh2J&(J$iw>TDqOmMpNx00%eo@qC8QF~ zg~+e0ia?uAogn{j0}Yg?p7W%8q%#cJq*qVBf+2SxUK4;;%CkS8Jz-EeUl)zQ6QqZ1 zSB~Meh&~<&33EC|%R9<0q&u!dV33tN-yp60BnQwvwnMHE2pN`0ZlPiENM4$9yYMHZ zDP{wB%8^IW1Wb&|Tz9jW0c#+g5CIg7@_?N{GeHG-5n0i5eE+xK@q_Pbiy}LDTamE8}wIi3a#i1a~fzE{H{wAtaj*q$JsJ^cY%%)bW9&e*8n-9 zoFSF0$|jPTx@*GE7y#CR;u@YCCT6@8bEw#z2B%#XfSvkDJnlgV-Sp=^293KW{AC6d z#(eCaV?3os@B60m{lQ&~AYuIMmdp>vc@RXB&=1N4zwe5;f@oO)+H@qqt=|A@p;ra( zZnZFTTZv2iLw*!PC-i{zUF==BY8M2sAf&|7NX-&#E*J9b&h<$M00?zYI3eEza$O%3 z&`s_nptCGMWojki;1$7i*U&rN({4ryPSExU0LOQQ`_R? zI=|RIxq42>q3)*}xb#qWWt?~V>_b`c>N)Y{c+&bt z=>+6FVZxFYmkGiiex-dLwQH(;JyJQZy+SKIQ$)7Ojyri?tE6T~)aA*X)`9_=VM;BO zmTy%vK8zC^UskBhcb|!LNu%CBj9t9VsYozYY#`<5qQb86XvJO`Ph>uwt%e-u(bCX# z1h@afOz}4^JHI$yP-8sYB(mDxwSRVx`jg4heY81xr!>rX`Mpc^4GxDlb{)wAknHaV zT8<(mN?wk8NYzR|YPBn5xjJOG+_c96lHawyQ9Eg9j#6}!k{lG46FanwuljCp(D?<< z6Nl=dEA&XEWlB4rW7sAJ0aFy2Hl_p}(P^8<9*EZ&bws{W--Gn$<#)$zQ)0|fhNPq) zW2*WPb;wmq+|1j|4)1lwKXu3MPe5?ZRQbrou8BGFtVf-daKcEOn z9H2yG=Fk3ady%g0TcopsgWB^sFUmCmL(a7Gaq^?(dqcsyrHuZh{b!-T_5gz0?Xf2@ zXu;-=L*QI0%AljYwkbPwrcPpDGxl>yG_-By#R)TwSR7WS!r z-&Xa&Y;&<(->OB2N?g~_+Gs_cH+m9hQjFIA2`G~Nt@=vI{cTEoR;GJOKs6@_2<&;Q zH@b*}&B23gW|Uk(=WXi*Okwiz?gZo(Xoj(uQp8MU<@GMSlHi-?VYqSNm{r?^Jp0r_OZI_WLT>D!yJQ&Bw~Ej4F?@wkI0!J^UV~yK)+0a$vjvLf~HaD$RW< zd(uYi>*D1Jyj+9fZdNm|FPzH}Ct7>URnGXvWHkBvAFs~wLQsRv(Yx-W5y%3W38IKh z1%Mk?YIwhxJo>ViOvn_56>mvEZjT0|J~Rn2^)gYifNGp}M9arG; z80YAbg0a7pdwVeFEZJnC&qw@;>w&aMT)X2kR$nt-Xhem7Dt8^8q)KY1#aG#stf@HN zqgq0aNE^k8tD0^;SxIi8hcq>Z%eG@&;H*ay9I>*^kwKTT2F20g6U|90cr#rliRz;9 zfBfUC$+dXNFMaKxOhd6pQ>Q>l_Ysh$d7HyZ1ngHAD#Ni!LErKQ&L?n7j^Lo>Dt>OZ zfV;nD`Mip&F-7CnpXS%%C$>=6vQSRa1g3EEL#_0!iiqy_eHHV@=Qvd$Fn_V|F#aI* z0QK!-XjLyhf%czO+-KHPBTCOaH-B~sjJVs^+jVooL&Mn7=~M_#=P1r``kr%FH`Xt1 zK-Dg9Xsk`Dj(gFYYw*sGS<93A3~UHqE%926PdnOy9{yBL+F{%6w1QF*m3fJMGdit2 zEw$)z=XqA59L?#^Sy7MH79rQ;?$w*xzZ~}3>k%8-`m%>IsHo zg)5y{zB#rua@;%gZ__?ycQ>zWV(NT1&o`*S_@a(}+^|;Ej&8i}XAfoED$&b`XglHKiU5+835JV%`cGwZd16p&B}@6KK5#54t$zOW{*<1L z-=LgokNcXSeuB{+UHMQ^%csJ3rSn{Cw(hImUp0%)P@HR{#Q7#dNsa0h_DeE+)#lH! zx?Z4U)0IxH^;{N*yf(5WixioZ?=f^b#_M#%>723mCT3;AeR8W^f#VcAyIJVyee;<{ zF9n{VtwMD47-aFHKobNTl&DP>dLlJZ3VvqlL+eIP_ViTj_M22>UdQVKvURA{mrm!y z8&IDty!+>KJP7JSfyn*N8pn;0uXRHgjmB9WWKG89$yF9JvAWj0@>mO-zh+psr*uAn zeafL&9sS2?8E%hZc41G}0sHe9(_B(*l2q^XAUJul_ScvAWM-DH4o~6^QzHVvOl^%y z*4Dhr;j>&Pxyc&jGpni3d$HfmJ1ST{mD60ZPs4XUA7uK}_#xcg3)H4rGi4nVr6-ao z?q12C3La@~O`3FX?Y&p+rQ^}GB5)2*)T<)_gD@pN(*@G$v~PT43O2`AVw0le4{F2% zlDT$?&)&9gJgPt2z$bB>PRdZ2VL6kZ*GI8={>mL~YkMos&3 z&Gaat%lHKWjQ^fScOGd*Vegn}_Gwk5>yz%-PIa0}rR2=TmCeqAzsAVv>f4p+a~wG( zSxU;UfYqgQ@)4z{NaoYa?5Ik5=+?q#no2G~px{2N8K;du9bo{iJ21so_4FCVqi4Lkzgk@B#ux zcq?F!>aQ}kJwx;2B7P_s8rHN=3>QnJK)Xf~;rGL64qKmJc8^!PSH2j}c@<#fT4ign zU^bexzf!MuscY{3#2{LvW?!@GHTzkW-vQywS+Stu_G9PGUtDfx$KIT1c-sM7Q#)@m z=_%eE;sSmnDxkN9)3gC$PS<|PCyKV(`dSPqp~oA z-4cxNy@)QA`}Ru_%6{g$qwM<^D@>try6nBi>ur3eHmxgf>8xXK4HYvg5tF^&!x9zx zXXg>kixy3b`G8PAE_=G&hkpuyeZRbj z7AI$EBQR0mMM5$j6^z?iXg_g^7JQozOG?*HWKt(H(?eWziSG?6yO@-67dNS><*cd4>ml z3Pdj;a+br2g}yc*k2gu>GllxI0js`nLB4AO_lFhgt@$l=3f-wV6p=sJI315C3e@L! zws+O>D~E<{hp^->;uZnk*1$@kv(9fc>{^8oPoEQ)Pb*>ngwh0meqFHkgmA8~ndw}i z{zpJ|;`mGsbJoMEHiC~@DWJnNniV%87A*4;Dty5akk$8qsWb-sktk_LLqaAW7ek8iKbTW(VWoV4DO-FP#PgDF zw#vXSG;cS>F83+*H-)fCRxr4J(p8zt36eOp}t&JRw)<)g! z-oh^`E+M>vC;P-ARZ|%seyCA?j2=DkuEi}v2uRIKB%b|Z@DGdhR`al=g%>Wp@^6_b zguDV5alcfJmf!Mq%YrYZcX)=Hw|@h?P!)puT~`3PM%-6wT6z;Bji%vA=Nbg*p>BNV z2@D37E&$!R^mj!#dn)NxTSA&4g(pSX%-{K7kzOTr{w?>yA5#|mDT9!h2iPx9+BlXN z-P&zubb$#2+5isNeqO2L8~>l>W4gY5+)QfqG+P4VYcFj9Z-gK)5Gd16!^MG8YKvIg z2ZuN42mazUz=2vy@ltx=RJCDj^C4s*%id|Ltvw0m4L7-U&Zgx0x?g!{%)f3kHB|dH z$Zwn!Q$VTuqnqc5ytTR|b&C~A<@e#G57s1qdTSfCKY1ZO(+TK7#*Vr*wFGN6|8L3) zKO6gys0rG)FMl6m#?#&h3g;405(G>Z85Q^WCMFMU7PB7gupb0#!%9}Vte|NVO;z_U zeCWVm04qcWPjb(00Y|&(#$Swo2WW>i1J1Cu2$p=Xu19}mAmImSw`8VWI_`dSe@0fO z{o>=~?s2snxY5OnW!&W9f`S4e>`7K(BXTvsat3qKe&qs#PZr3i?jvdb@ zX{&MnTRn#j&Z@T+&+0KZ`CGSo0*(X|Stbu2RqJxDyk=oqK08KC7MbBa1>DlI6Mj*1Y{6!4433T9US; zJdfS!(y>tJU{;yD_wR`7chasqxHfLrcy{V70kk#2IGJCXCk~DwlLS)pX=9AsWsE#W z8-0@XJ%%RvERi%Tchb=334cna{FG^E>~p_0!knrp;Vu%My!wH8mY)YRw^AMX52>yo zir2E-b{X7$WxAb=<&~s-uKh~dRxgxob>2!p<-50)e~yt}O=krkUgTrL0cD$9to>!G z&|^(bk@L!vVS{TQ_J@IMGUW_Y1#X^!?(-kVdu*Xu2dS9LN-u1PpVBNxMYA1`6!h~n z$YCkTzI4(Dbhb1QTxM_dq5ty)Hzf-YW5cFsC82;meGoLv@+XaP2p0%VsHnF&&6gl@ z4|B~)v8w_}cQ~^I6uwpbyoficS@6u7o2|pRN!Z)S9`fzsY&hnMWU+64u(PnI9O-%K z&;4M@Be*NHItkyuI}V|&@&lmO{QzvXti!RSHiqdJ=zw9)u-;;BZqYtwsI7j&yR zyGdZdZhQKG#$&Ie!pVA>8>G#G!S*?A?)%EswzlV9%>JCbO^Fj|R4~#|WfAe#0=x42 zJX!IrCJae8nH#^j_+r92qgN^7GoRk_8pAJQN$8=UfD(^m-2w+pCvGyL3PRD{$=Ifo z9zmM4=@qH3ll;N^aS+|KR6?K1L>dsKc{6sRqBlw{3dr=!+ao@(GL^|sW9AP;C5{JV zb7t8sMVIiEGp|12TpMq?8FCC!H(Jx~P@#u530x=WdyJRfd?7yzgBir_*oPGu9;?@Z z54DV7@;sPx34@5*Gf;tzq$g*O*=MtLu`VI4Ln zyO(w9WZ`QtvrqG$_Jrc(*MRHGeZ;=c`ny2WQ%D`Lv6z3^mzbtG&n1GKtd+q7DDIrK zRlsJEdo)-Y(4Qm~*$*qBQ+F zvB9CxC7M_HqwOP>0l_-CxQPRv55zj3kOh1fJNz1nCzP_EU?+MRKIlWSr9U50U`j|} z&7Pl}CfT<+5eMt(zd7)J!FxJw0c5Y#BnAMa!Mo*7KeF4VO?d50OV`02^g&V!(H ze_F{ZsFB}IHK5-?rk`a%8zckP9b(bRg~Qj$IMHw-j_FNC+6N?-4!c)N{*bNu+n z+h(X(Waf9?6*$@6&+tf%#`;YcB$?#FtA#hy!CnaEtVeMwOm!2-lMFbM-syD`q3$MP z1OCtbb2Cm{;4Qx+RyOE`m>Zre%Wd^6;WjXovz~I&QWZw|e0Cp>JBylJna8K=Df-OD zLIeL4<}^T^Cz;h{;ap7DhtcAg_DlmzdpE*Z)8AvXys^)!+Qh3YBE;+Dy|!7C^z@%>;||1}eVoDDtrLv~ zav>uBh?6U_(fbTuyq!J%GEGyM4Tq`Nik@4usxipAr?arp{d$b%B@X)jGS+VFh;YY~ zfqM_rt4J};N|r68eke{_R;TqE+rg`(91i#^<8Ah0uj7s?D zIdl(dN**Yy#^P*OU^>hz(-XX+N?7(~8V=Yh_*wY&kvjd5yA^Ccn{8L$O65y+qG zyE)QAu0fi@?)W7$-d81)`BURQO${M>g5<{MM`4Y78lWp36q;Sr4}FRXHEXp@H}8tC zAEd@`#;m_JPvN;e&cOIfQL<{TsJD{oIc0)jBZKV8h#hazF^RSOCX;{j}OETcqzOZ?LN*1V0CJ_(UE` z6%clxeaL!df&8X@(+@bGzbv56nPwC`wxYjLzZobSGqGMXFzYpHswIaV$Fe%H>ND=c zOS04EE^tDw(k|qXlY+2eJ9@QmKMuRPX~o_bGb8VMZu~goSs6>5U4>7j&$5D>a)`;; zgmX#fuuT{GjGd)_x~U5$(zMOjsO!Ei{5iAVPF=N^6-8!@##ztXcBHVHTGACQqWJ~j zYwetZ+Y)^&fm8m^W}U2sG|X0rSOTLt>+}8?Z`Jk3oJ7hm+EBsc;V0@u1>1qyTX0)y zv6-5M8f~lDw@Ba5X4C*;0gVbgugjr7-ChZk=h9I+!cr0dlEH}JYewFPvkt(PSWfsY zMj@T#;_U4`&}tgSlfdwr=m^Cj(l5?tuofakTZ~CXKTD=bmS#R0ann^^xUPU8;_h^C zawG`MHW)wm93fv^cG?gF?a5#~A+=sG6f@X>i!EdEUlp(N zve<+gGY@_gO;(RPf)~zfT=ZF|AfoA^xL?JAPTV?yuk@`$#~tQ8lsodq@@P#K&KS$v z<&-=J1W^I~=5?mbos|4(WewtwujOKKZWkw?O>!#@*q38ho|<@=@ozURzqh+J_@DIQtpuGw zH5_-^j!cy0el!$;jcnXZ77FP-9DRfzaLsHBB){Rgdz%s*FM!TGCAjt9hU-}A9MQb7 zvHl(85dmp3ui_R9>fh?o*3-NXzt1wQpkEg1ERTvd?`vjBAkXG>|Jj@tG}PunC(N4+ zSO^aH@`QMp+(Q47E3Nmy$x_HyB~!#<4d3E9>VyA&8f5Q;QojDMcts7gX%fAG-GJ46 zZqTK9@uk%3ZgZ=!q0y;4pLL%hksku*E|$~p${1QF+-Ew(+a`Kxz0c3t9+!#;?nB5b zdpRY=bYs6_SIP2pqjY}tZ`Fg42C#>w4{MuZnI=LEci21s9jo8Vd$`Gcl%u$jnvNaK zd6>b%=r^S6Y4ocq1kty=o`&g)gxtWrjc8WW+{8y!w#B{WLJbTFNgWKqO6?`U=$P7aq;=Y5;7QW5sPLC4fGVO|3zig27{ccC+A}#{-U*4e zlVS^ix2{|6Gr%f5m}7b#5d3rB>3PPatMyUXDY?m|cE6O_HqRmNSx12)3Uc{_17K(K z_W=g)KB2o@^|%-)QqRQLHA3)x?`(^UpievLJ2U=oI3BC|9NNQH0M+<;C4K;?X&TtE z>T}WRpeebh4a(oSq!Y?U-b=?#8(DsHTPa&jMeEiaZF_x?|3x@Z!_qihfdQ?5zzoY&d2`~*Lge`2~4m6#v?)ScBa$Aib zPl%+tWAAeQ0$SSsDKFRjCUgyhL@Gd^P+PDG_mZ5LH|>4z+Y5z#tcN`>-op9S;?T9A?b=4NpU6I+WaJbUb>G(nj|0UtNR>_@I@l zgRv9wq7-H-0N4*>)6s5uPTYo1< zrU3+5Hq`LBXotPDQJ=zAD9CzK;&V^Np?;MPQ~FA_c-a|0;*=#=wK0b!>FyH?FN?Hk z#+e&H*i#jeVfo#6kh|~q0%g}4$4PZY#-hfn?eF(M9DYE5g#x?q>^r-F)}v_Z)iE0s zsD(|{8SWQy5pma3gZg>hP_e-ichZUL_7AL*?qT=oPfWOheM48p$^cK1vznp$6vULr zN5-EQcZM!TBGCahLREK+Rr-T!FIA5oF=iaMdE9w`!PXz9VUn`{UT%qj!os%f5!TY@ zR0@-MILF9J^kr!6*W>f9jHDtSx+k)1%?B}w`%g6?DKxOXHZC`4n2l zMeAUWPdM{t7XQ7*-F?&^={<1!#k%a-9@Gh8+`9^NG>WqtYHpyhxnDOo$OI-_2Na`iyHzCAbIn%-WB^wPFdL*kimE#3TDIK{0bExSa)zQ z_W(LK{&+8!69dez9}@*2?P&2TZD`{^hsMKQ``D(!A_Ve|4X-3$|WY}KKQ^z>Bd1TXw}^F{t5kD%Kg5n@&VwE)K0 zURP+h`eQ@I|MdOC(e6404f%}cw$=L`=aayYMXZ_g(ftC8I^l;2J^W_u;6T=!1H}hF z5b0|rTOuwPzj~cqxb1?JqQMO2RjcJ-|AU4!(m?yt`Ak*1z| z+5;MxRTwq>aR&E4pmkL2oQvDt})t5ON-kEw2T4pe4UR4iq-vi)T$CQ|7-XGclQg{k{%le1}J{h$lP zO&47Glj`H==`%z?95No+Ccb#J^HT+yy&WR+kNF-bthK95U~J5aZ=h{YuGi-1;dm2^ z)JI2f;K!S#%0W;NdWl>M?Y8lIxNmObmoD3FL?)K|fkZZ=7 zshSmrxCKzv-{f4ZURrHD_T~04;l0yfAZMx)Gz5PDl0I>nUO0YyRG~xmONj7B z`YzZ+8@(c6Y%vsCy)Ehgba5-)yh+vrFc3vN1f-V(?1O0Al)a;CSh$X)cKz=h|))!xx5Rt5FUB zBv1h$K{)jvs&~alRb{vLI@UtNCWGPfCG-^`N$AafY;z_DCa63cU^F%EoLg~0uPz@D z$Fj~K>xce4)c%_ulQVh50Fx?j=uBqP({Kq`U_1Thf>~gZopE~z9341i;N{i>1V-c8 z161ETbamQ@yHeDeqpXkL@voCR2o=CYU?AtbpdY?gYk5wkcx0?B_Wh`b!t}C=Rq|LV z5Y{Id5{-U0j>CNU5%xCYSw36McaaC@K0VDpXVi*;a}`O7=!012&o}(4uvz(_*ysZ( z5vjd&JAb7E4g}xC-vj(kNtHYx9r9Iu5pXo6(%jZ);GfDF5SaP>To1!IaUcfBfO<>s z(fYs_asR9jsM;>K4p$2DfO;Y_P3WAS?qnQ4;EJy-@Z0vS&VgBk>iVbIVQDb>&v*MER_@gPY zXU&?w5bmKG&pnHE)e=kh3Pcs5k9b9Pf-)5@7jI~D8B~1rZ^C!1rhs~}yYaC-!48@2 zexfM%NyADP%=KK~eZ~p|cSZH>Y@gP(Pc^Rn6hOHVsf$cKM&4cccTSpow-o5>JkWHb z-OzMf?viTMeeLp7U{bwMp}Ejl z3-uQt*#k)O(QU3amFKlz8*zPS8`;yUT;WGZ^QL1UdVi84Si#ZPMP=12PM-la|Lel! z@)(iW?fmrb!M%7d1rbYYBgit)%lP08uO(C1kFMp$a#&E+kui%tneb5x_Or0OO7NQK z?90KD8crdiS2AX=LzjcgYyWgvqvL2`WKqBay`bJGWvwVotGnW#s+muskx7cs=~iQ5u*h()Z7=_e|O+ zY2j|K>0Z}f+oqp}$OUT9f^^&Z<&`{NU{(@EaFBC;jKNu6^v(sVWInaq7#8jN25K)H~xosi9?Kauay)$=(P8a=s zdEn7}OmwE7l>=CYY(yIN?8(=f&ugTox-~sMr_XhI&pCGI^q6O8`m`FrnnV6&tkfo; z*hg(o8(ip|yZtJK=8K~vpneZ7O;HoWD5I4ml^;)JG53L)z7MGAiwLm-sE0NuJ&lXj zG#$Yk`Y+eo?Vn5>E(pA{TXzKe?r?kOo#`E8+-I0w&)anGB)VCAItmSYD1AimY1WlK z+A6*ts8|E(%R{{B1;5G9|8PD(BSoxU0b}JiTqc}fx|hGUatXe5g*2a;_MNM9OT8IT zLKMLY?wu6oXX+3a3VRhox@}4|Fu^)W=LSd`ny=RRwzrKtvm}jmz|0SHz`a~b9p#Tt z%eD`jlbQtAaAVme{;!|Q*1D|jMAjZc=8b4%IV*&|Z%Vj7g^4?z0K{q%w^m4uO7K+O zYiW#voKfDRKHG!!yYCi1st>vz&{{due7HgO^bvPC!DiPb5^wA=H6`HYw+rl$LVKml ziF3xy@#NR5_Ts%n|5Ya87h;u1lyZdXp}efZs-zdoqm(zK;D@DSIF#k`cG)1ElT&eX zh#;R1IQMOGP{Yw3bzIJL)E_zbY#7|k3iA>gJf^~J_C~-sq!hhuzKdNA3PlUJuY~w& zipQ;HrugOhHlxpjPnqf3cqW~eDIw<>=APS@a#!=i@(V+ALDPn{tA6b(8l59A^IC(y z2TB(_6l}-2Kf6AxGNWy4yR{XLy)79(E?ydu`sw%K`^bc+q5ft^rih4ZiL%ntj4NCzX+c&zMy|;;6+uNHory)_$2ha z{yV(y3V>&o%U_h|`NF-jhHPrM^nC+g>F|IP4>$Y29f~r(j{3jY4Y9jsCwoE#I;{xr zqUWR{L`*q?oxg8ROI#_M1Ds;b>6rj3X+R7Fc>J`|Ak!TNVUe_;p3_`93V;%2CxZ<) z(t!3t)g;=B6z$EQ>svXCSz9~5_L3&iK}=9eG&CIfJjd=YGr%Hr8&WlAL~G&So@C;h zF;t+tk~h^1>%XiN0KH(uTamoNw-__ z(o7zovN{ez7aX^%QEVM)*972?s3h+9rSFQ#QqH~^z&_X1)Sh13j7eRh&!9r5J=9?p zsHjfj*UHr958bsIkU=h0Z@!JclVL$+cab7s@&JjM-wK&w-Vqq&-Eq57pfW9<%4X}f zc4&y`huo~y*flsV=jW?@w;N?L(YGC(U-JxbNG|h>mFBX(Ey{(aI)b;#E_!f5Io~5c zp650k)CD_TPat))N1$!1?~@vCObT zW-7DVZJos4Fx~Kap)f^#U1JS}*1YRdK!AWyayy7Gs1d2R3w%BMO#}0z;|94|2&_y{ z=$CN4=CV|@?ehXMzhgfs0zd^5g${Z1??1LJ4FQqK^ivE}HLgm5Pep~l`M%g1Y`26X z2U|f80;%7Y1e?XM_olVcDlh5iu$JOpe605qadM(N|E>Au00Hof|D-J!M%%}iPJQ>J zhvzzHOV^6F)I66f1x<7yej56V-u|QBf0A|0e^dSj*>T#O=ES z2;+SWAc|3u#DAiJZGWbu;hEHl(zv_>GHRj$sh#_7-2@^fxC8Kc5E_$gbdE2}e_+4! z|9Js0Fsb;>(ZF9U(LKAq>TT;4M1YEWzNL0Y=ktn0s_-0(N%?K9_ppZpTXG`iGJi=^I|wSx-Sjo|z?s}jg@m@l%)#})H5 zZ)~=ch_*%d)Q)D`&$Zb;DD(sTbr6n~-~+B;`fJ2pm5@a9CJyhLx;Lt;&0kTpR;gfi z-Qbm(Rk7%oXRl<*x8tkjalkmzCf7y;aacjO&e5lB(+_>L;{eu8Ed#M!Eluu1uzkmj zD_>4EY%YW*5cp&iotLPbL8m zBLT~Q=42ZspbY$*aENNuOdfMzocf(sM)y?ya~TIJ7I>QI6IQT0$=0Ey5dg~T`Q=}slGb#Px|N)Kj+%JL)K1v$k$;$VGb;<6%CZ{>?8!54RggdA=~22TO)hK zLmrR&$!(vqoW(L4IJJyRsx@!wW>yt6%jfKq!UsDiR13xY8a@0z8Vo7Y+yjls_iE4E zOYfx!-Soe!+ANsorRkoOJXM`$9O`dD8z=jZ{eI|FF{4_qA=iXmbKnl;h1>PrR`Ho{ z3+WikUmW^lf1PFkqxG@46`40N?C5pDmUTcq4dlxf%wmSFl^p8`EZ73jx5`rTZfHg; znt%{Ug)Y%TYZ6C~_2K%^*z~P~dvnG6u0iZ0(0K57;3<;3qNzdK_yr&R$yZ0-w7oQs zG0U}jlz^&0j;AY~E{(wx)4Mlc%u@8y{x^u4L5L80B^?j&ylsOrE3S_suz;Akggr0& zYM^mml6Kb))_viRqwA2lF=0_2-cg?y;aprU=&&)da=Rc!w6JC_>&1CS(jIKhU$4V| zR0thxM+%-z8eOGaG>y0bXT5qD()S5G$A&vB=WKdy)}hZ!1=^54Th3ZB0!>XiJpeL^ zOD3iGKI$PDFpeidfu+p9SxFoNjDWM?633T%8hhGsd-E*!^NI3$ z+r!zw+{+oVKQ%cEP1SJ7c3&RsA|e~s;E(EgnP|N8arH?fs^f}Z{T}B~*%8f@hl|s` zv-U}`#Lu_E<}CX0RXu{enHQ?W2pO)aOu9&_Pi9BbfE{lLpwonJ1l9)s9VwWirRBvV z;Jkn6#p6Fqf7^=*GoA{JxyvMS?In-+w^u1+26$ZF-6!n5ACU(n2;ULt1nUhq$>2Qm zI7$E(dQJm7m-~@~S%X{96CxVK;?A4#W~a~VFSvk$M-yNs7dvocZ9iUyH9tL=pwCgmCV0)+7;bPm_{7D%qYssX;tz`N+LOblR(IaZ~G)2DNT_JElQr&vzb{!We87jF8k2a(b*agp>G^FG#I%^!JNQ%~*>DHjDbA3Yqiv(A zt_-6oz_6?X%Bj0io2kw7wZD_YGSB+AzaDQKG#lOZ3;>(Tojr&I_)8yvDuO$^VD=VU z!;V}q)gFUot^=BdLP$4a#cf8JbjEKbfqfhd0Jk%r@3%@P=wWt6-&KsdTYZV)13OSwlNSqQFcGPfehCQ@XiVrB%5!^vI+hYrt$chs zw=M@q&DYk;%d%zaN_-5kup2WoSTiW7v0gKyX}TgT&~niasUttCkg==Jf!v>}GGYUq zv|nBwmF*2?7D46`%RqbWM<(##h-7Xd0NOJXS%P)WFBWlp`A&YTlQKX_Nn}s z&%;na>B13)E%gzz2e{09v*e4Bd6=NY=&4v|r}z+mo<08^db2EVcWpRln?e#z1qU2N zK2_V#apQNN*R|!C>Jl!53|$Jqd8+cuZb~UktykeE)CPQ_uwqo?YUzyaasJK?NrmA-3x1R|M%_E{C_B(sQ^V-Q@4k z+Cb9-Ic%ZINE0UQKv)DA2S=`FUr)c)+wa!$SCtdd3r(k=&?^li&AUanMuBFF;Lu#hvgdRjV;sr z%dmaD4~aK87)K|dP%N-K&VcC|U0uAXl@CKxlDl8!G?l;oY{~QWXh>M39PV(Ht1CI3 z*#!ZfHm{!#fLCJkt{8V*Y25Ctm6Ns;&06tVNSDVslvPA0sx>(<&2DB-A;ej$=yZn4 zwm1=(aQejY!J;92BhDobm6(StMA7<~0j z%elg5eH?>zei6W<<862rux#3?1H5Akt}>AM@>BA~(d9Y`rAMp&P4Z|BAuZ?nDEW?F zS!~K%m+!|}*}%+9kLhWgs1A#wtGN(lh&YTn8}zz9^-cQmejgd3de++-stx9d7S;;c zksN7X9Zg?N=mgw)TOENd!Y>F_)d=PfacL36cP}4L#!0mB#Iu8^!g%|W9Px2sNx%%C z^}O4?a;2mfI|+N5BhZGN0ypoi&VKs^)Gwyg(mdY)psX+IY*w1fEDO&coJIx3b?2>3 zcH9SDk|I(Jq47bYjU(skZh9@r7X9m*=fM&(@dF3vk#+gBv`B5iiklYTK<(DiuwOme~!s3t%9)yXpg=cnDn6=R9mj6W8 z`xok}=Ng@6bG=Dt^}3bEv2#kiNzwI-1ElJ&dvC&bR0rD7(bw&Sa3_UHtzoC!UguB$ zrshJ9C)h$Hc&AZ_NbF~qHo?^hHZF4&H>q`{9t;IK98j7 z=vc{VJLL(@1B zzXQACeU<-cDx2%v0T2~Pj9j0J-hlA%yVthE%gOb~SQ|c^GEsz7WQFwO z?M7^OVrb{FYH)N~YuhWQS|SXV7rYyKmDL7l=GY`iCwtHMu-pz(sjp*UPfS3#eeLdg zHYGF0;TJ%s0`=D_eCQZQ+i%lb5n=ksx$`FxAir@1vyQsq6Oh7aknAlHObQeVogA+E>3Su+SI*GUmA_!^3ke$;m%@oK z-V=)t>G-5Ch(6PIJ6_zy>V+@lbF%#qeL+ogrxldN>q&rzKle(0{&H|IU_`QYX1*RW z>>l`0cJENjPgfTpr%;r#Vt7EJ$=Q&em+9(^k=VNh+Kly&CcZsLa2uRh`I0Bxw%>j~ zfjGZgzv#L1y&s<}n_Sm-e{JrlmQ5vw^G5~0G*+l1m%s0~rKHIq%y^lTZjXYe7qwcw zu%WgKSz@=`da57_nF(b~pZzX~mCii;)+zR{f%!=2o&pSLmx3M&pq#Tp8xE#%n*0~LRuTsWqUPlA{b+%02e}5#EUp5}cy|}FXS9|L9mkf(T<*xJt zkXc!$|57EHdEK`M=YNpW!Ind3<##-pOkwOrC6C7Xnjo29%5ub8|0XOg^CP~=FB*o3 zfA&#?S9;}WY!kA^_F~V8+e@c;v74?wOm4w$fp2P)s^TjJS|{8{6~->8^OhpGWZ%|> zgIt{(KaZrj;zRkx9<<_l`TTP)JSNDejZ`wZC6nFeHr%b9)M=I1u zU*AK3`z~_}F@s&}9v&YixcIXxKtKs5q~0fl-x4L?Y36<`XA*L_7a1TY8JZHV-6X8(iyp-p>#N{q?L8ym zx>T3_*%$tcsKOPurEe6^G#@ulIH(9HVRtdZ(N+{lGwB?&)$p%_hB@(7Pxu38k^Sn%nV4k6sYM*1XM< zKRzYXG-PidQ!nD&{2dFAUH4>3f=+@{fES(vi7$RaYG0dPaHd#ce&38Jxo_H-dtruSnLhRCbATfm zee+2)lX@Sp;#In0?bM_IwG%(GdB<(71pM~xG24R5L6N~w5Js}p`u%ske38XF&1 zqQQ+}D4p0IX_)o0c&J}Q04;vK%)tC+n7{xJ^vbeoQmYBAMF8Xc^K7P`f%HW;3r$4o zCH}p#lp%>XHlPdIa8Mf+r;H7AdEW{ixEG@8Z=)dl@7RS?$@BVyx#PXN1V4wnxYcBa zD?I;NH)5tY?;xh1xhCI15HyDKIgR%ZKFySoh`YUd$Tb8FwkyIiT~9}B-IY|L9vs*e zjRT2Mhl7yGWqDN;@|Ujn{yyZ`x!AR4VKZ+$uaj<|a62%zFcb*dn3{+gh-kg~lgvZf z0USk@A>_k-Df<5^b>x!6%vn$n^=#I6#GWr)3E*>$p%J+2QG(TM7QRbLu z`a_r%i_qOHUK3O?$;xSZZ55Qwl-5qYgToXSJI_~4xA-TM(`l~u*wye~$m;oa zrPnI4NT$Q+!r-HOnojONqSJ^uk}Q)2W>-_fI_d{6xFUzZ`C z`e*U+8JIRcEMwdouB1d<&z-D|5X})lt@>gr@L@i}o^^lu)_?Cg-hy2oz1hebVz#*$ zlq6(T*9Fq3unn`Yf~R5i0A7-coQBKA8WrQ`r=g9ZIz-ohkpigN^EAw_)sO1abR=Ut zLU^t2;hihMj`eNqs+6>`&t%bU%uLUiO{_A5pJkk zT#X-=R!bxt8;$(z)NRjwQ5SacPww=UgxVKdo<)_((k*7ygZg~c)6lKrp~kWmBDH6|spA6KiR5OCFr7SUUif64@eX=szO z>FGXQj~YWp7+^t=8U2va)jNoN^ecsmq?IrRM=&h|?EEmz?J3;sBF<%2lq2~;z)6MU zQ`Vw3yz<_#&7W{%$FKui&R)WXM?q@=+y6+}zT`YMpuV(5ZNXiUn7Mwa7M@+r2P^Vc zk?`gOD}tQuDTsGfKlBl>hZ^h|1Id$%XHa0r#wonO-;vMy7+`NbQ~xRaWX=#|Rrhk_ zHOIaL$R1pgh4n`%*Z%E~rm1h%>v1t%meH&WNG>)9Q@>4z}L$xx;qANDY}I zn4PSkWr@7a>om|7Voniq%&#sSPe8(@Qd6HVGHe|7Vg&Zf6qfDhC(WuxFn-af_gN+E zi1uWz17s6FkG6=C3&c^neY}Gh(*@%A$x=ipkhs0L>NvA*gGA`UVKKgm+GkU)X9am9 z)LPjZzIHq#&1~e}FEnn!BC%ZEiO>Hj$SzPQcv3BB{GnprSjMvhGcU?7vq4pCB)go- zNP}1X5ogb9cmLE0q|c_BMR6lqnQ&3>8eArCRa1ax)7 z1PyzOAzgtd9v*A+@^9@*GDq-k>*cGV_+LhDX?~o1*nb9L1-i##!0RMOF{1Qy+>|HXoK7QoAEp9bj2O+D@tPLlF%`g_icw)IN(f39-oNWVy-Q~D@y)v(R<=<0-B)wyct-n`{NO) z_uuNl6Fxe75rJAh2ktkP9SXEd8HzKsGI$#t}^1^xoFCzwn@Ku?E_H}u` zd(TrKU=|4uNZ1??L#mvw=np>4kM$s368h7cx$ka7cN*e+KU>CX41Ad`o#X(Xx~1;A)H?&?hYX1Cu6S07Yx2~(*`6F<9X)R4BLWen+I6XgW|a}A zik^&quBs$**_m=9*=x5=A%LZ^ohCzObi6!>afJsG4rESP_*Qsv9vdPQ>&4DlEo0eh zE%VpBi2#a5#XI`{MazuVx~da^#<8x*yeyk-^;X%Y1qt-i9x+bMgwb@!{*(~)W)sfu zlmIi6yw`5`%<(nqXv{}a6FU@SWUug|(?fKW2sX5TYYt+LCxg6d94*_`Qu->BltFJ z=3>CaS|YIn8T5UOS@ou)rZ_1Wd(khI(FaesI^M58{;AsJmF}x``+jwvG$O!*Ir8w1 zf+uuA^^a$ztW6l5Js&=Fu;YW7zfHbO_#MQGgp$7P=lkIdOAWr6L(Cg!iGJr3Le_xY zO8Wu$x`Q2eWkK)SR@|qPxNSzu z>69r7f?aI4b5~Z4rA1dhcq{ghY?C}RyPiY-YB6_f^!0c9tgiWWeDJQ8qdIetX&pDy zmtX(hpH3JRY@5Q0bo()L?q#M5?YysSpZv-LIlXmEE6(Pep$kttdaWEP@;h%#ISBUL z!K?Q89stbqZh-k_#4XUeri{%upvw&Jj*R2#MT2DvN*WLRLDb;Zs$rAM2$wK*2e|^Z z)dy;3VakaW%q`H$GY%QyeC8Jl07x`bVXEt1#LxEykw;?*kt-jj8Dc#%}K zgVma0&&O)kjBv zo1>uoYxAZa?vA%;+|P>%^`eqn19mxx@HN6fSlwBRroxNE7B;>nG{36rbm*Q6G7)_? zE1*eT*hE2hq>ePL{`5fb`zwYJ+Ynr=Yols}oa8%^Fv{-<2ty`lI4gsE?Qp7jTg-Gn zt*21nig-Q9Qt13py}f2+ZAKC@oFqC!F+Qc96xO@`FDDW(dfS1UF4G7_)MSJW!qKjP zsTdy?+dO@Wz60TUP?NMs$P{izW0ff45J$YV z?GO7DJBPk8ykLK`kv+l~tWyki2o9%5(nDFvGi3etX3 zt!Jh2`KUDWrMnz;%WEa=v#yMw zl%0y-qMtYY8_>U2C(l~>Nn56wTPx2deO#2Rwh8b;n%N#_4|q>7-dB}$3P2iA)P%Yq zN_hu3?y=&(R{V>r4ZG|&n|=H9Zq?-xsy1+m+*(33$M{j3`NXHbl47ZNA<>%m9z*fm zvATEre?ss595QtmwG&bIbv^(0g9(PYp%Ff?-~g3B(Uk7X4?3W|Aoluy^rt1G8ayjvGIH zz)1XY>ra%`bRn5qxaJ&-dtyxMKc}mLc^8-a_jrkul*38lNh2Cvrj7cTgLeAO%-j4u zaMNLz(~~;Vm?zXZl#3_Gt&f*AEU`6QoVqhBv6H!uea&5$#@B@{TdadbpT}VY;^YrB zk619W&IYY-G*Qhs8o!tDTrbCZy^4t=C5GR08@cF)r7>L9dMN;nIut|2;_jzf*xrEd=tk<~{@I^a}`>)nn1ogG~?WnLjim3pS45Va$k`3Fx0wO_D<3NBCH zYWz3L1YL7K(}u-koMe(#`oX=P`fm30LkHR9QYC%7m1L55$_PCv(Dg~uHPAV^7^3EU z0g?o{_5Hmy_r|oX*9{afI&5{NrTTv%AEEER87-)3T%irDYyMFNFfx%kqA_{zl*als{cP5vTiwht{sSmcinq!O zPV5}7v)+O=-lGwmir7+)VNaTYa^2I&?g=)Z9j=uF<)_^Z<+En&pl8DOV4dFgVCvfL z3Qn@t&TVdvRR#;)B2vU$~Obk0#& zC#FH2-4puO*Mp?=ijd*-&&6F%4XiOWXFwV7L8wZ|tOBiWCWRDTe$*S|c6l4{I*0Jz zpR?9wo6vawe<>vKG!c3dAUYvtH{iLhU8O)v8;no7sYJKRCqlbk&G4P}52lB$&zPcJ zMw&&}LmUL9Ieybgfi4+1+oAWqh7$i(h(Rk}iGSUrN$9d)h6Zo+qTVG;B}d_F_BHFD z4bqe;o^zs(kuXfh>g(50@+Oh7M)gW{Px5BzLK9LA9YJ3fvD%zc)V$v;7!kAfn|J-_ z%Fu`yLNzqghj{+GDv|}4Fie&m!Z)jfmBk^oXL!^~FZ-}Ja1T1ciE;*uaKlYfDBlNf z+`4lmun9FZ@dp9E0~`7nw3dpB*{vsv)Ew;k%xPl^2EAF2C)I_hk29qH+2v~zSTX0c zY$66UT3bT~Rn=-Lcds6@vlcCMx!c+NzO6Z11kz*a`|-H*YFYN30re{Q18i_h|2DCh zMclitN@69oVI1uqj~%~?I6C#(tR)A5u=u(e` z2}VYhq-+yRa%UTw9a|1YozHlKD-FIHzb|a`yC2YM(aIC3kIL8{1|LRJsLN)_P~){z z@`Fd&OuyPBl4#w>{{XCNw9k*y;aI!u&D2;u>GtV?*9R~RAEnf{marjhDF_)&^A~fo zk7r~21EbBLrzB^HAXjUQ`w_J5sG7SZ)ky+N#qj}(yLOIlY6nw5JS68SEW{%`U!KWPhpNC~w zbE`a$kRohQii);0e?^IKTb7gk@g|fCk6oCSuyh5Qy$SrteF#hH5mdS04v$pj8?Ho` zS6%mRCkz^xozyE#%qo)ug^T}oB3#1ibfe!TDRl$fpSo5TVfx9gDXks!>}!y^)nkfo zyAP|8QE?%u#ma`!sq}G|k4HV89J7`W8Nz0|mDLxHhbAXO(!Wft6}g`d1}dp~D6811 z$7ogAB)hCt<*{(&-j_RgyvtT$f&~Njmv?zH!Q$vfEPyaSca;QH8@#|oyr!<04ow`8 zpD%K2%@Q+YDxFPS6r-ll)Q(gQten$g*@R|0y{`xpWXs2sau#EPeIx{4WH;3Z3Iyui z%K&_kScz~Mf~GV(HrR>F{`6Q>v#Iu%rW+8gHsmxK9FPq|-e3_&C*aCuFt2Xb#h%64 zetgrsu|9FRJCC;Z~6PM+5!w<0Q+{pI>P0TLOgCF@DTe z(rCqqhV<4f(eHBa+d(}%6`N_th^y2C88c6wJNRtl1o zkh?=`zllrCDpm5F`%vGmuG%#c2P`$ZUIj(_91inm63s)1^f`C(0Yl>nTGW@r`Y?R>?D4FS+S}O;I!g>}9 zHh30V0o4wIoWK89<6>sZgzLQrhetN?Xi#gX9jPpaQ_Qvm@7y}<8UM6He)nD&Sw zK?7fMGwU8;?qmUAhW7cu2shzQF#TnrlBZmb<&nJ=+Y3K%FGGS(aUD{NTPmQp$SbdV zvDvP*BL<{Ue+IMb(nlf9w1c$o!Sv|vPc@YLf2F2S2l3A&o@?aR?oi$FnXPFgf1u&g zzPi_BCtWdw&2=B6W6J%_%)%!5K}qy^+S$v;QXm4*1pqycg-;3ANep2nF4_?;-ze3L2|eaedKtP5F{SKrUGJ`P>=klBxh)w-?-)JEX!@hfrfkavMtKKeQf++Y zCKvc(K?yrmWNegFEDQ2cxO?#`^z#|bC(=iVe-vg5b0NGOAR2xds)%pwqeEXSFZcL z*j~4JO;HUMxgj;#ShDc)6}J=5BB1E2<1#Om#D!qysCQduTpPbX)3 z?-V8d(-i;QE^!1ga}njCFt(ZkF)AVFU08$JNPX!fdAsNx8(Ud}ANUG;?m>y@uE|!9 z*-p<@SXGo~V5`{J2hNi?g)AjcaUhWKx1e`cRIXkSXC|6!&>^1eWJK^!tzv&Y?3*br zvqxaL!oJD~O?RAERaNEDh4L4!08JWAsfBiPFLi>yEZof>uB9DHImDmcojN(tWX0?ydU{)0{1v|?uX?4_X zr5Y}X!-pTa$}Z;)a->9Tik^K2%giuJYeo(bQd!h#I3|N{mtJd7Fh{3@D-RH z6qb1mRSwbrXrV_>_-Xi21@qxlS;wa!@a@!-=fL`h`+;>74U1L&nj;O~+dY&RIJwt6 zCroqU0u}V>F(~79zeQLKsu$n-p%iR?&m(f_j|tgIKKbi|3vn%XJ2ui z>s)J{*G?>D$#;wyuWfE=x?+{rLD*00OQqqSQ>!6nKJu_VgBAap3+Ql%rAHu|SH=g5 zjBe|tP2ErAG~I+aegwtN4KcH(JS9vXnM-5E2zGq~s#|*%4p@c{0T7KBzzkbm?yjOF zvGx|@gk^ui3b!{pc6#)teEp9N^P;vrHDc<|%ujNxYoC_6-IOHVH}Y~|5X+X`Nc^m< z_(rb{#(#qg@u1bfN~rLV+nA-u*BrMw;SrA8U%^cCfqiu~j?@TGqhEPOlQ_Z} zhFGre8r#d4xk@Ih!eR@Q5|?6EdObID!Gxi$`pMZ&Ojf=WlTf0p9}-f7>XM3^QV`c0 zzq662!MP)E|4Nc@lji|eEKwt3@JXx*$P%Q+IOA&f1lRs>NH?B-7fvqBGqA89A)WNF z%yQGTk(x@xX2~|F6x~vh2ZK&I0MO@@26bpf!v}*dOXyIDLfe`A&PsuR>sxRB=DBa5 zyM|v1WF1SRUz|f0@)zIhGG&dWtIjUjOLJn4_e-ccMk}{(P!v9W!|saQlNNf(qnETA z!P%nN(tq2hUOn0hDnEd5-pM?+7kvdCpu%bTEOiuH_oM+<){@gGaRwip687bPd)n`^oBg&!y;vI1aP#GkRKA?+HwboaH0W@v*=bz(#FM@?ewp$9|H9~ zCIMo3i5*ode`-`JtY#!Z?sbcCK26RscSg?Op#uf4&H3g&dkw=X^SKh7Kf;@PP{{HDKTbcUoltkOp_uOqs5O z<_6M`)3*j4?}+CVJ_As+uZ-x|NKF`sfpPz;|Al?)Oop=S3<-NWm$WfD(h0c1EkuVo zB4M6Roo7DbgstVmfAVN*&pd6E8U4YBjDscTcPqhm3$ofOF0mkL#98sHwh@AZV+&ab z=ZPcQwO9qPqC%MFA)Z57M7hwKdkInsLg&oWUmwb0&``L@LC|(Zn_{}seGoZ(yTl-D63+P^w zIOo(|Z|W7|xEm0H{lcUn+cXF@qp5i2Y-|SR^ih9i{n|Hs__C1a`QI2(r0M7nXfaIj z^eJwTBH(mJLPBkqFcKEhdMxm~k zkEp`YG$9i?qQ(e2)X=r(ys64hN5nkTN1bc!NlAfCkr_EXdGsF^fF2EPR}u#8xmZ?* z^mQ3x4M~A^xObUWc0LJDv0-Cm8n}z*^Ag(6`i|0Xjj|}CF%vybp)GO)$^$~6JiwAl z{|gW2P2m~cQn4gWEEg4Qp2)pM*r%8l__E`@=Sb0YaEJ8tchG*Zbv2g9DJbQ`Q^v4< zxfPPvH73t6sn-OA8I4ZFp0VWAG$*a{&rqrH!bQ$mTwLV-V$%A!j`M06r3wrx{)HAG zM4x>YcZKxV7j=fw$jJ8vhr0JBCk!mJjmu|23+GPa*j-$KW$X9ip1!8PKq90h_dWGz zjY(d^R)#j~>wkE@2JWp0)10*6zu|~jQ96ybmHJ}bpFvpANtM@gFO{or5*_um{*=r^ zOt_G$y)B@sg=hN4*46A&*~7nRcnwqHXvTJEKH zMu)AYTGCRV7~>)NG5q+HmnvONYpao2x0&3VYT#M|J>0YgIwT9&mgq{}36gqsmX6^K zG4z|+LPUj!hUJy`*p(#G;v!l8HY$&N8c?GxqZv(@1KqB{aTGTOHlH4kONZq$<+Vi+ zARendup7n%^*8{p1+}A|zo2(Ss?Rc%&?=zbJE)4WtBoIUi+w}t zJ}4f!P`h$|RUqjFC?*|%RoyyM?QQ{SW$=ovWZpE;kRN)iQ*KWfItW-+-)7Utaajaq zOa4ReV+A-1gYtGB?YehNSX+DU@;K~^XttWz9fAR3Ow$vD5)&rg>ZicWBkAHYO!881 z3Daj~7~rmN!HMih4rXQOaz@M$QBIfD)&{*~iw8l;KV>sKqqv0gdTwGr+oscy&k0-h zF@RiEqpv0Jy&R3M^aBxQGVV7T=d5O%CMm{GV(-AET~~h_5w-k)^WA0sfU4HwnIo3q z#rsU;i?P(UYZ_=B4(;-3FOIx8ZU+S?vp|QRlb^|r5z%VO<00s9=h8-M=Zb>ggV_g` zxi#+wE@!r9_6tD|Vgr7Fe|tSO!pij*S3t!;&1rSi7&%r7u*)lfU5=D#AC3VkkY68U z2A&I{2uP}DjL56)u@2T6WE!uDRzB*VjJ4`l8Iqnky5kx-dM@xQ)K8dN^d9hEv zsq}A-Bd&v92t9l}2op(56PhrGrvAoMc5CKdpQ9$^747!TD7`fUsEtLjHk!&WyQ+4= z*A%6Rg6`6uj2;Uu5lG@rnEJl{zopcvKlVuE-gw@~KT$}aXVEmCVasaTmiHpsH?)l1 z>}%qDul1V^SDshjT=W&u{J?Z{JDHw%M8Ew}C{PjBe2tX;bit7*$(TU(kuaeC4k0QG z3~bLH=Us@B7sS&21%bA9ZRbxb6B+b>~BI$PD z93YaH;IZguLdSjE$mRA4G|QD5M^QGolfYm1bpllLQMRku0LN+ z;^B(&!=1$6>{LtDyr2z~QjfP2oW;qNxWc;YVVXC&vmXsyfJmViM4*!<*kODHN|CSr zQgmR@o)8K7(ZT1(*%$X?LRn9c8J1)=E(}R>1+lYK-f}js*+Pte;Lo_au|8)CQVaKv zLp{cbaCI7*2YZA-`*(AN*aV2~gZ$|MakJWYM{g7e73)^&xA)W=tNg5_Y^n??nb^7w zhzxx`Eaq%z3h6e{ty({+jqQ!EY6&qp6egpeC2`dz40!Uj9iTQzwzt3-TCLNgTqUUv z-iA?*3Apl6nAQy3o)1+s5OhCAS`iRnV(Q=AFA&7r1s;g$=N$jS<4xzGeR|VaaDP1P z`OdKNb`Mod^Y_O%S;!og)`VsnB<|`;nonIdI$tgVZU;7>(Kz~Hi_hPXKetlM*h%QP z7MOKJAq2|#5kWzPSQK7eU4`pw?z!}s)Bp@dSK{Su9SbbG@9(cAxOC7O9-gSO?%xHj zndd?b$?B}Q@@sWsSchHF%;uZ%4|1|#Ga@?RnTzy%J2KHunyZQAF z4MI1_xkrei)1SeW+zQ2vqW0(_=S!7`)kxbi1NsMQ#UUXSMDXDehkaqqppoD}L&~&KO(YvpRT?Wf8M!gbo2ClkN zAeh9}_$@da{<5tMUx^kRFIfDPf=BTCDkU6lCzs4GZEV88tpWp`x%r6SB`>6k8pxTH zRasDn1lEqeWFTy`6WfIEx-q|3MYEuNd}@}@^dGDs1Ts&GVb(mPODl0*hMXGv+`V4Q z5m<}|l#(#1%D!o0dFMP4)A!bSBAi zf&W-8X-V<AMhc;<&xymctPI-wPd|pTFVUmc2JR-Li;QB}(%Y&&Byh!RzXm29LCyLeN9V zJ}kq!l0>tp|&BvRhY+G$VpjB-2Te$!Eq41iM;94V4 zhq(Z&)^QC z8$+qf+d*h%<70L+)!HXZR_D3dQk5*#P&$;VNbzRc7Z(3fQ%Ktk89koL=Xx(IKJJmoDI`D{;0fEX44&feKhkIE;pUM4}(jnhTx zPUhXJKZ*bQZh> zl-k(V+j*lif=+#ofhQ7|5N8C{fFfaF;~eMtXI9|IZE>O=CX=$!!e`wb>9{{OzH4QY z?Td*0r7kE2x1MHe1Uj#Vx&L)Ontbv6d$icCjSFP^TlgH*19qE{dGZmpE@2gKp@L%v z*G4*E%V9K`MD8ryY|OEYw~)mt^j8npn{uzh8^+&&X0iXEgXTPj(&UHQYDWD;|6#_l=9;_MlHLi4p z%j0~E*>{5UBlc$MOg#iId9q0O+P_)UxAy?7&$72OvUYAZ_ub=huQix~D+J{UGlMvq z2ui|UTQYGRhHQf&Iw7^9qz;2i;#dQnLXUG^{jMw3qE8wjHP+BRO{KTCZ5f_g82NpM z_!$~+z4&McJ z8y*qVZ=Uv^{gNUq4??Q+N^`0N99-V0HaE?A3{#_gUIZ@%I^)S79;V*a*|gA(?>}sEr&FJD{ZJVM&-;mrhx$RjP6<~7z=74 zTgMRHl^vl1js=mg8F{&cz?-+eR_UI@2VV+ZeodAYc-$j1SqmJIP@4?%JqpAGU~W35 z{@J?0%E9*@f|tW9+I_KyARIHV5tEl=vcMa@u+y%ct3GCPOtwlffT9ssnZ28Rrvbda z(3TL#SvIC^!suuU>cmTgU2crNLvEm<=4A`0&9At*LXQ}lX#BpCWox?smx=?a`W6bS zT#JhNtAZZHUn+XG(P*)RdlgX~hRraqla5(m-&$YNbGYW}KLrK@Hx^J}a8A6&l3Pw6 zk}9%n^o;1+Wub_{MQ&v(y?#Il7uTF%kmZI*xfa6+Q9xKZ^!m!pMEyOp!K*x?ED>tO zX9~8WL236PAIV`*uY!n`VWPU&0H5!rlk?-`FSgBspe_)@6e@&_+NM#AXg=oI7xY-h zjz1WzIWZ0L_}b|pl^evD%7oQgAYh_NJ20_TR`**reP%1*JDg{l7o=vxMOH&8Ehbh2 z5jWA>#_sCGu5w=Uyj}DSIMfI%LK*;sK_14HjJ|+|ThPbKxu-}wttXqWnVBYB%&nI) zlTmPyo;wFoJ{aIqE=YllqioCbC!~aT& ze1Yl6CHZuXNf2^w$f|}QjboLMHE$`95FUbg`OuN4^jY$lI{}Ta!sqa3USo0&(<8IK zu>}jRz@A45{nrYPS%(h>=REt4pnN&c{Wi;0eQr}>7b^rhASn@r2L^dR>atN5BSLc4GczKp^M~yao?{O4 zT20@5t?)fIlk_^0>uXx8V@}M48B2I&*UnFHS!faG@aDs>ya3#r-TkKaNqNr^CJF*L zc@}>;U`G<7Bq#)BD}y7gbRyz2m?OE&#a%n!QtU~CwZs=>aSig$P8VgV(oCI*U z(FTJRdt~1hj*m?-MnU9~gRO_RB~t$jl+6NzD+++JPpwwJQDh$}*3!Z;<%Q4L#EfIN z(XfxRAv=S8;C922a;ToAHkhFL#=kj2+t*1N2K|I|7cwwah{t;($@1e@0!{T1%VFy( zP*5g}Bcym<&Ym5m2jUpalkQYysF`BaL9a=tku(Ss8hx|f0hPFPDfMC82MGQiZ32+u z)ZFTk9#Nx;82Fe%>N`A&{jrD7o@h)yF9YY(P+!IaX}7iSTt)hH#{pk~c1 zZE=3NUv_R0UX1x9L{>Gz{Yt^_ZN}};GJREz5U9e35=#wegEU0UM!wzNCDwAI%4%J5 zcipoTRi~js0PjRFs7yKOMIXN`cL#s6zXhy80jmJYdo1%XZrdYGfKe;rDF3vLar)OHxf9}O7H9!a?}@!3r4fzeO7a`RcQCuHcC!lrNJdHw z*PY#qpB@)E^hNUWH*<1Pq=SYrUQ$=XE@w2(;Fw4)W=WUs5EEvy*AsS%8DL~WuoUNq z2Cp@)-Myn9^qyBH0jC+Z4k`o)oey>_Tf!nvZw zD$LTc*{D`mJS`Ji7zxNj{Iaiv;<+B>ij>!kd8b-rGPYF~HtrP3J94*g67ln2XU%0! z&xL`d=2?+Or3M-$2A{+&SkH<&Jd~DYR5>B1SZ#gyv?p#6aN>%;XNstoIbx^2S=~(g z>of=gPhX(cPRAUKm#W;ung4)%Ef34~4f`i4Jr--r`|p&5iHoG1`jWbFsgdMtu4`fA zPMC{Z$rG@nUr7y!N>&ru;G|W+odC{4{cVq|wO@1>KNZ}1x<~wj8#s0$eoQSYWfO_8 zpOtb+*oAi?_B6U2WD47SGcUWyf2u4inj_uaA%Hxw@Hfe)I965Th0e?{Ci2lMv42&l zSIkW?s4M)(j~rBInkj_-J8-z*wzwW>G>}fE7#gVP3Y{W?74g|%*|OuTnI>@C zHJ%?~U(4C(eqc2O6BW3!x&kASD9d~*C_3^OV;8#w^oEkD!TmIVges<=lxHjJgk{SO zoyl#MvnZ=cif23d@1=22wy5GD(PM^3XclwyAp*rKe+vU0o()u=uJPMDP6eACO;y)1 zx+>{b;!eufx^?F4yc-tfLWe0)1Xsf_!zlxJ#G+mT%keL7``K6VG)$jc)hIT{S?xr= z&KE>%Zih1H_o#Av`#yx;koYh<_x0fo)L{t7^Yr)nIiNfz&ldyp+Q*2Vj_1I9%$Ixw}G+R zO_g5bWiwrwHl4p~O9U48@j71dQ{oEJ_{yF>JyxeA*8Nn0Jeke8-5V0A){XLp=vpPR zph%13U#wFa1VL%!7(~F4Z14GXJytwGsL=fK6&o#4KXXy=(HU1atL$dKm%4YuY!XeL z;49I87%tLGMlSr8AJ1zDak`o#&nv{K;a|As0~m@Jkzy<614VfQVq=W9Wrb5Ex}5Kb zCS!I72tw5`JYqbCTt{-3thq$FIp%|@{)wQgPryj-&S0CDxR(|bgj=7PaDUk^e9~Gs z4}L%+kI;Jlz<(X@=6JTr?f${c%T}Ih$XkoqUF}?m+l zquprZ2Vlu8NjgISmO1;TDvs7^DC^g4dR#5hSPn)@t#0FTjYQPFzZoG{Lxc6&hE!Z( zpwG8AIXx~#!I8K~vp{`-T~)NNbbdwjW{q)x1vNCKfvz?UqLsZJ+oox&+#yTh%L52J z1PH-*(=7&62%VO7(|}1*8^j+~Y?Zzi;qUM{&d9~v7O5;xaNA&st1zv+lnsXc7=0r5 zp8%kWr9fF!Qmj4BkK_3w%lVq(Lk6+@0z$!R_Ia5hJplr+?aX7)6KeF9@TETi6K_O` zl(NVO&Fpkqc$Wd9&z*NR?)#(@W!i)G_;+46ZrUIZz`rhS&*MUP~fC&2nwObG9!pDjbh^YM6%lgZ5D zrYbvAfBHDt9E08IyQZEAB{f1kAOuqyZC8W}s9%qgCD53O1IpR}V!FneCx%(b+?42p z!ZmGe&X1~-rRgt*wXd1gJdX>9pQI*h`>L#KFvL+isDIzI3Z6hNYMA38H;M%a5CdZ0 zSxl{XL6&tjPfr!<4tKg#cH+cda2=%sQ+*oVDMbP}*Z$h)xz3E!b5Z|L-~YLIpx~)^ zURzkZa#7w4!q^%b_Q|>Gt~ruShQ4*L27}%7K&D@Eq(Xe`{MJpv;m`8_fwE!Zk$?j& zoWz+poT0Q-8XZq8B@Pm{NjkLW}On|iw zFtwglwaVS4@8_bV@V}?H$QM-`MImBaK<}vO45CA;gG9(!v6ar1{6bT8g>^Yx_+ikm z@Yp)L4X>YFX2Drg-zvo3t5K!TH^T@Jp-c~XR{CKjODleOiCwvMJvxD5@Ohz249gr) z-#c{DabiCEr$qr_gv~P89se;*T)#2`Xdu&xyz^I<&o3Q?L!NkoEv%%K-nb1@L&?u^ zOS>f(nqSB&Eg&LoK5qx!JR4}1?7<|A^jbFK@kgtnngC)506q6fD7tsI>&Xs1(w z!hd4)J;PytJ@F~u=o|2n9=rR86&-Tg$)w`E&d7;KDgp1Dut*=_v+N-M3a-k$3NBg~ z=4vyl?+w2}q-EY;9-pSW*Qg!N_7-UrxHA5efku6*_M>PRQyUyN11(p}Pm`aeQjLy#E=4D{BR0fpf4sU<7T zf0HUpwq?TLefomjEj*!N$FcB%vDaLZ{fo?PC+X9td*7wea=;5CyBZ05Z^Pg>Lq|j- zrr+n87-Pkrug^gfQj8_jV>pl0Ow{LRxG3o_=@>wGKNRiNhnY}WR^yk0q&p}ZlEf0Tg6`6x4WvfG?5lOpY^JbepdDrexSFA(6IN z`8=gvpDC!J&$~AvA#iC)*VR2Ej?rixgMRVW@E(TrH3`W=gs&|}tD)O4YnE)(m zy5ART;|@v8t6IqB5}26XPx{W6GFXs$=u(xiTJr8o&b$@Hile=}yL!4yVLIh)bpj`z z8e#;%!hY_DFDhsWL&K@Q41wFfeNAd%Eq$dmS?kw{Dc{~L{lGjJe+LG3ZZ%fZww#o4 z3?#&o9B)ySqI_*^^E;awptq}eCA}?(9`C0CjH|#f`aCm=0saWnyfNExx=2^kfo?3#kpxSXbnYm`*A@~#!c1lY(b;$hos#GnBm4)1diD`;LneAK35R9O znSQHou>Ri1{TF|9R!}edBb?U#`4o=-B-W3 zAS~cP4UOTqv`eM0rmh+QsQf4@1Xv%ib|#MkW%AO_Z&>jnN!dzN_OY+Pm`^o`AKAVy zO!*!?a$K_JyboD>xk6rl+DL%TS3cb?XtkJc6pX2}wQW4IUyJz7-!LLx$)zcc zHJODiyr~b@#RotC!+SO_C;x`Q@fYW<{GO0#%~g8UW*}OA1i;dLMM#I@A}3`W#}WW1 zlDpJqewnYYdc`JCLi)%3i%99&gHPl(y=MNAHH5^N5}{h?FLB{@4mu{lx_;*X^33_9 z=OkSkC+N2#^p21k@nKDAbjX=*Y2(lp5{c?oD}k09@CYy?UeL$enK8uVy>T9k+0@^* z8W5wtEHBn$plf=~awthv(x$pld7FZ&+UK5o`93v3NBG0S>A+l~r+%BOZ2oC&n$w#H zmisLc;dcb9t>TR3(b;N{CQT8ZxrroOgtd2)qDA*rADJfTV|L}5*V;68bteGE$o~b# ztN>Mpi%^Q<>ygLA7u>&Hn)icZBCJA88Be04#pE5Q942C(=D5Yc>@w6|Y1y|9OAu0% zQzJeyfRAp-sC6#r%-5(=PCg3#4f;7pn-iYO!J_ttUHcs+$1HET$Y;QM(@2e@g<&6N zMnWj7`Zm3q3gF+e{K0mC(!`1`+D3xc}B-$WDs+s?<#ErFs=V^EcMFclzCs#|BV6Ygg~8&_We;)_x^x^XbU6Ny1olL zni^`pOw9gNl_jE2URtW~xB`o_*<3HdE~{19D7mxL$lKm9(gi!CPmXWp^^enU4dYq^Wv4%&$12F8hY1;i^a%-iv$!8A$WZjI zmP~;U;+@(0Z--t_Ge6<|c=4CMiBzv76ATit14crc5M#oV zJm?rVBSr)~=*aZx*BN~Q!SPWQ^HSv3K4AZff*4(H0J&A1;F~=xd+u>>IvD%fnGo - -Components: -- API Gateway emulator (local) -- Lambda function returning mock response -- No external service dependencies - ---- - -## Local Testing Steps - -1. Navigate to project directory: -```sh -cd apigateway-mock -``` - -2. Start API Gateway emulator: -```sh -sam local start-api & -``` - -Expected output: -``` -Mounting LambdaMockFunction at http://127.0.0.1:3000/MOCK [GET] -Running on http://127.0.0.1:3000 -``` - -3. Test the endpoint: -```sh -curl http://127.0.0.1:3000/MOCK -``` - -Expected response: -```json -"This is mock response" -``` - ---- - -## API Documentation - -| Endpoint | Method | Response Type | Description | -|----------|---------|---------------|-------------| -| /MOCK | GET | JSON | Returns a mock response string | - -**Sample Response:** -```json -"This is mock response" -``` - ---- - -## Additional Resources -- [AWS SAM API Gateway Local Testing Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-start-api.html) -- [SAM Local API Testing Introduction](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/using-sam-cli-local-start-api.html) - -[Top](#contents) - ---- \ No newline at end of file diff --git a/local-test-samples/apigateway-mock/img/apigateway-mock.png b/local-test-samples/apigateway-mock/img/apigateway-mock.png deleted file mode 100755 index d88090d41f8dbe0c167cbe66f8929d31d87fac3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59925 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 X{UR@}*KhM|7=Xaj)z4*}Q$iB})pk)x diff --git a/local-test-samples/apigateway-mock/lambda_mock_src/app.js b/local-test-samples/apigateway-mock/lambda_mock_src/app.js deleted file mode 100755 index 3d88f415..00000000 --- a/local-test-samples/apigateway-mock/lambda_mock_src/app.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.lambdaHandler = async (event) => { - const response = { - statusCode: 200, - body: JSON.stringify('This is mock response'), - }; - return response; -}; diff --git a/local-test-samples/apigateway-mock/template.yaml b/local-test-samples/apigateway-mock/template.yaml deleted file mode 100755 index fd5be20c..00000000 --- a/local-test-samples/apigateway-mock/template.yaml +++ /dev/null @@ -1,25 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: AWS SAM Template for API Gateway with Mock and Lambda integration - -Resources: - # API Gateway with MOCK integration - APIGatewayMock: - Type: AWS::Serverless::Api - Properties: - StageName: Prod - - # Lambda function to run the MOCK - LambdaMockFunction: - Type: AWS::Serverless::Function - Properties: - Handler: app.lambdaHandler - Runtime: nodejs18.x - CodeUri: lambda_mock_src/ - Events: - EventMock: - Type: Api - Properties: - Path: /MOCK - Method: get - RestApiId: !Ref APIGatewayMock diff --git a/local-test-samples/dynamodb-crud-cli/README.md b/local-test-samples/dynamodb-crud-cli/README.md deleted file mode 100755 index 3cdd52d8..00000000 --- a/local-test-samples/dynamodb-crud-cli/README.md +++ /dev/null @@ -1,132 +0,0 @@ -[![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) -[![AWS: CLI](https://img.shields.io/badge/AWS-CLI-yellow)](https://img.shields.io/badge/AWS-CLI-yellow) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# Local: Amazon DynamoDB CRUD Operations with AWS CLI - -## Introduction -This project demonstrates how to perform local testing of Amazon DynamoDB using Docker and AWS CLI. It provides examples of complete CRUD (Create, Read, Update, Delete) operations without requiring actual AWS infrastructure. - ---- - -## Contents -- [Local: Amazon DynamoDB CRUD Operations with AWS CLI](#local-amazon-dynamodb-crud-operations-with-aws-cli) - - [Introduction](#introduction) - - [Contents](#contents) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Architecture Overview](#architecture-overview) - - [Setup Steps](#setup-steps) - - [CRUD Operations](#crud-operations) - - [Additional Resources](#additional-resources) - ---- - -## Project Structure -``` -├── dynamodb-crud-cli _# folder containing json files for aws cli DynamoDB CRUD operations_ -│ ├── create-item.json _# json file containing create item aws cli input_ -│ ├── delete-item.json _# json file containing delete item aws cli input_ -│ ├── img/dynamodb-crud-cli.png _# Architecture diagram_ -│ ├── README.md _# instructions file_ -│ └── update-item.json _# json file containing update item aws cli input_ -``` - ---- - -## Prerequisites -- Docker -- AWS CLI v2 -- Basic understanding of DynamoDB operations -- JSON files for sample data - ---- - -## Architecture Overview -

- DynamoDB - CRUD + aws cli -

- -Components: -- Local DynamoDB instance (Docker) -- AWS CLI for operations -- JSON files for data manipulation - ---- - -## Setup Steps - -1. Navigate to project directory: -```sh -cd dynamodb-crud-cli -``` - -2. Start DynamoDB locally: -```sh -docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local -``` - -3. Configure dummy credentials: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='eu-west-1' -``` - -4. Create table (CRUDLocalTable): -```sh -aws dynamodb create-table --endpoint-url http://localhost:8000 \ - --table-name CRUDLocalTable \ - --attribute-definitions AttributeName=Id,AttributeType=S \ - --key-schema AttributeName=Id,KeyType=HASH \ - --billing-mode PAY_PER_REQUEST -``` - ---- - -## CRUD Operations - -### Create item -```sh -aws dynamodb put-item --endpoint-url http://localhost:8000 \ - --table-name CRUDLocalTable \ - --item file://create-item.json \ - --return-consumed-capacity TOTAL \ - --return-item-collection-metrics SIZE -``` - -### Read table items -```sh -aws dynamodb scan --endpoint-url http://localhost:8000 \ - --table-name CRUDLocalTable -``` - -### Update initial item -```sh -aws dynamodb update-item --endpoint-url http://localhost:8000 \ - --table-name CRUDLocalTable \ - --key '{"Id": {"S": "123"}}' \ - --update-expression "SET #name = :n, age = :a" \ - --expression-attribute-names '{"#name": "name"}' \ - --expression-attribute-values file://update-item.json \ - --return-values ALL_NEW -``` - -### Delete item -```sh -aws dynamodb delete-item --endpoint-url http://localhost:8000 \ - --table-name CRUDLocalTable \ - --key file://delete-item.json -``` - - ---- - -## Additional Resources -- [DynamoDB Local Guide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) -- [AWS CLI DynamoDB Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/index.html) -- [Docker Container Documentation](https://docs.docker.com/reference/cli/docker/container/run/) - -[Top](#contents) - ---- diff --git a/local-test-samples/dynamodb-crud-cli/create-item.json b/local-test-samples/dynamodb-crud-cli/create-item.json deleted file mode 100755 index cf086c10..00000000 --- a/local-test-samples/dynamodb-crud-cli/create-item.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Id": {"S": "123"}, - "name": {"S": "Batman"} -} diff --git a/local-test-samples/dynamodb-crud-cli/delete-item.json b/local-test-samples/dynamodb-crud-cli/delete-item.json deleted file mode 100755 index 208c2239..00000000 --- a/local-test-samples/dynamodb-crud-cli/delete-item.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Id": {"S": "123"} -} diff --git a/local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png b/local-test-samples/dynamodb-crud-cli/img/dynamodb-crud-cli.png deleted file mode 100755 index 35d64c933c8a602ed604b74f571846ea2dd3a047..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56353 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 - AWS Lambda Functions with DynamoDB -

- -Components: -- Lambda functions implementing CRUD operations -- Local DynamoDB instance running in Docker -- SAM CLI for local Lambda execution - ---- - -## Project Structure -``` -├── dynamodb-crud-lambda _# folder containing necessary code and template for Lambda DynamoDB CRUD operations_ -│ ├── events _# folder containing json files for Lambda DynamoDB CRUD operations_ -│ ├── img/dynamodb-crud-lambda.png _# Architecture diagram_ -│ ├── lambda_crud_src _# folder containing code for different CRUD Lambda functions_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ -``` - ---- - -## Prerequisites -- AWS SAM CLI -- Docker -- Python 3.9 -- Basic understanding of AWS Lambda and DynamoDB - ---- - -## Local Setup - -1. Navigate to project directory: -```sh -cd dynamodb-crud-lambda -``` - -2. Start DynamoDB locally: -```sh -docker run --rm -d --network host -p 8000:8000 amazon/dynamodb-local -``` - -3. Configure environment: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='eu-west-1' -``` - ---- - -## Lambda Function Operations - -### Initialize DynamoDB CRUDLocalTable Table -```sh -sam local invoke CRUDLambdaInitFunction \ - --docker-network host \ - --event events/lambda-init-event.json -``` - -### Create initial Item -```sh -sam local invoke CRUDLambdaCreateFunction \ - --docker-network host \ - --event events/lambda-create-event.json -``` - -### Read Item -```sh -sam local invoke CRUDLambdaReadFunction \ - --docker-network host \ - --event events/lambda-read-event.json -``` - -### Update Item -```sh -sam local invoke CRUDLambdaUpdateFunction \ - --docker-network host \ - --event events/lambda-update-event.json -``` - -### Delete Item -```sh -sam local invoke CRUDLambdaDeleteFunction \ - --docker-network host \ - --event events/lambda-delete-event.json -``` - -Each operation returns a JSON response indicating success or failure, along with relevant data. - ---- - -## Additional Resources -- [DynamoDB Local Documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) -- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) -- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) - -[Top](#contents) - ---- diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json deleted file mode 100755 index 1c47303f..00000000 --- a/local-test-samples/dynamodb-crud-lambda/events/lambda-create-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "body": "{\"Id\": \"123\", \"name\": \"Batman\"}" -} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json deleted file mode 100755 index 12090c66..00000000 --- a/local-test-samples/dynamodb-crud-lambda/events/lambda-delete-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Id": "123" -} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json deleted file mode 100755 index 4ccb6ff4..00000000 --- a/local-test-samples/dynamodb-crud-lambda/events/lambda-init-event.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key1": "value1", - "key2": "value2", - "key3": "value3" -} \ No newline at end of file diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json deleted file mode 100755 index 12090c66..00000000 --- a/local-test-samples/dynamodb-crud-lambda/events/lambda-read-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Id": "123" -} diff --git a/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json b/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json deleted file mode 100755 index 074569d3..00000000 --- a/local-test-samples/dynamodb-crud-lambda/events/lambda-update-event.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "body": "{\"Id\": \"123\", \"name\": \"Robin\"}" -} diff --git a/local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png b/local-test-samples/dynamodb-crud-lambda/img/dynamodb-crud-lambda.png deleted file mode 100755 index 97c6c748c03bd4e21a844b313a3ee421403fba7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49967 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 - AWS Step Functions with DynamoDB CRUD Operations -

- -Components: -- Step Functions state machine for orchestration -- DynamoDB table for data storage -- CRUD operations implemented as state machine tasks - ---- - -## Project Structure -``` -├── dynamodb-crud-stepfunctions _# folder containing environment variables file and json file defining state machine implementing DynamoDB CRUD operations_ -│ ├── img/dynamodb-crud-stepfunctions.png _# Architecture diagram_ -│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ -│ ├── README.md _# instructions file_ -│ └── state-machine.json _# json file containing State machine definition_ -``` - ---- - -## Prerequisites -- Docker -- AWS CLI v2 -- Basic understanding of Step Functions and DynamoDB -- JSON for state machine definition - ---- - -## Local Setup - -1. Start DynamoDB: -```sh -docker run -d --network host \ - --name dynamodb -p 8000:8000 \ - amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb -``` - -2. Start Step Functions Local: -```sh -docker run -d --network host \ - --name stepfunctions -p 8083:8083 \ - --env-file aws-stepfunctions-local-credentials.txt \ - amazon/aws-stepfunctions-local -``` - -3. Configure environment: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -4. Create state machine: -```sh -aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ - --name "CRUDDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole" \ - --region us-east-1 \ - --definition file://state-machine.json -``` - ---- - -## State Machine Operations - -### Initialize Table -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ - --input '{"TableName": "CRUDStepFunctions", "Operation": "Init"}' -``` - -### Create Item -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ - --input '{"TableName": "CRUDStepFunctions", "Operation": "Create", "ItemId": "125", "ItemName": "RobinOriginal"}' -``` - -### Read Item -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ - --input '{"TableName": "CRUDStepFunctions", "Operation": "Read", "ItemId": "125"}' -``` - -### Update Item -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ - --input '{"TableName": "CRUDStepFunctions", "Operation": "Update", "ItemId": "125", "ItemName": "RobinNew", "ItemAge": "56"}' -``` - -### Delete Item -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:us-east-1:123456789012:stateMachine:CRUDDynamoDB" \ - --input '{"TableName": "CRUDStepFunctions", "Operation": "Delete", "ItemId": "125"}' -``` - -To check execution status: -```sh -aws stepfunctions describe-execution \ - --endpoint http://localhost:8083 \ - --execution-arn "" -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [DynamoDB Local Documentation](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) - -[Top](#contents) - diff --git a/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt b/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt deleted file mode 100755 index 2a80ded2..00000000 --- a/local-test-samples/dynamodb-crud-stepfunctions/aws-stepfunctions-local-credentials.txt +++ /dev/null @@ -1,4 +0,0 @@ -AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE -AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY -AWS_DEFAULT_REGION=us-east-1 -DYNAMODB_ENDPOINT=http://127.0.0.1:8000 \ No newline at end of file diff --git a/local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png b/local-test-samples/dynamodb-crud-stepfunctions/img/dynamodb-crud-stepfunctions.png deleted file mode 100755 index a5ddf9ab9f2ac9587055b4e971e0256deeb33fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47727 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 - -Components: -- Python Lambda function -- SAM CLI for local execution -- Test event for invocation - ---- - -## Project Structure - - -``` -├── lambda-sam-helloworld _# folder containing necessary code and template for Hello World Lambda_ -│ ├── events _# folder containing json files for Hello World Lambda input events_ -│ ├── img/lambda-sam-helloworld.png _# Architecture diagram_ -│ ├── lambda_helloworld_src _# folder containing code Hello World Lambda function_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ -``` - ---- - -## Prerequisites -- AWS SAM CLI -- Python 3.9 -- Docker -- Basic understanding of AWS Lambda - ---- - -## Local Testing - -1. Navigate to project directory: -```sh -cd lambda-sam-helloworld -``` - -2. Run the Lambda function locally: -```sh -sam local invoke LambdaHelloWorld \ - --event events/lambda-helloworld-event.json -``` - -Expected response: -```json -{ - "statusCode": 200, - "body": "{\"message\": \"Hello World! This is local Run!\"}" -} -``` - ---- - -## Understanding the Output - -The local invocation provides detailed execution information: -- Request ID tracking -- Initialization duration -- Execution duration -- Memory usage -- Container information - -Sample output details: -``` -START RequestId: 7a563ee5-369d-4d7f-bcf8-c3cac3cac68f Version: $LATEST -END RequestId: da370caa-cd29-4d9b-abf9-e81ef7f6f769 -REPORT RequestId: da370caa-cd29-4d9b-abf9-e81ef7f6f769 -Init Duration: 0.03 ms -Duration: 44.43 ms -Billed Duration: 45 ms -Memory Size: 128 MB -Max Memory Used: 128 MB -``` - ---- - -## Additional Resources -- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) -- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) -- [SAM Local Lambda Testing Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) - -[Top](#contents) - diff --git a/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json b/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json deleted file mode 100755 index 4ccb6ff4..00000000 --- a/local-test-samples/lambda-sam-helloworld/events/lambda-helloworld-event.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key1": "value1", - "key2": "value2", - "key3": "value3" -} \ No newline at end of file diff --git a/local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png b/local-test-samples/lambda-sam-helloworld/img/lambda-sam-helloworld.png deleted file mode 100755 index 05a982171532bf5ea377998fb6f0f23076a65cf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38055 zcmeFYRa9Kh_ArPA2(H0B0fM`0aEIXT+PFIbk|4p|-KBB&5Zr@9VEHNWKk@16C{ zyv&-1nOPSex=z*JRl9a=uX92ae&L`W4K?aVB#O(7s8L*mupG?WM4WoUnvf=3Jf5;vYoO8((X9GVuyo)}tu z6b$@_-mh6k5;)j8Ex`t2>cTqCE^kb#4AtND9BaKpM4&xbZ=Q?iB>TefFnF{5us!Pc z!hJEuy)z2(f`bqb5~3UutApr|-5xR)ftrhrQJ%|V5Q4_)gkk6uAyw&boR~m|NXWiE z*}9VZQnUbwpegseewodo$DsWRK{|m}_yg-FU$7qxgx$Nr%{vG*)2N1|WJcw+E3|{b z>_9a2Qu_jY&Qkkke0{vikjO3JM;HheInpR|sH}NJ4_ALC%zY+<+D=WH7)i40pgB%P zyJqDGFSU5EYNvd)Xb{myGar0t>Y$m8ts|-Grpq85~lbDz7WCV#j zlDFUO?IyH9(WM}*6Go$zk1UnUFv(P38bZx_ENQ_fLJd->u&)}J>yl#fI=5MJR)+5w z9s9+BVX8ewZ>x0Zkl!jE7@Ojqug(cSw0VSP&sAY+V)$;7wx?ubla!2NUyD-Xo|5zi zT!rR?zLF7D8GZhxNJs8q5XIw~MwdYOtJp(FKY;|Tl=#QukYg^~(HVq;ZEz@O5SN}3 z8Yf#lDG7sooq!(QP-KMqLx{Qw;?JDv=T$f%m{otFPFQJ;$u8$8`SK*Ah;3{)3k2{P!b^Q(e9q7)nVJHE#&+1K?JX_*DK_=`)`pKl+r#@n_~53 z8u2>j6|@yBR)*5d>hDM#0q*VFJ-YcRc6NN?ebBADCA5r&Dn;hF58t~&0($Ww2zVpd zjmqbgu!;2?6kydrKDKj;J zRKyNkBMyBUXZ&tg2UmwTLRU{p9m{$>NZ#sbY6m#BXB7~)geNB_FMEhMu7qDm_*QAx z`f)}RpatpiA<_iNRR$5K2!YLLtEqvIZkdowkncL*^=?3c{5enIgh`P^{oSLG3O=3G ztnwrVCL1tlz=R5a)IjF=f>4Qk2s6FvWsk=8#mpWf?M-4QwmmfHTWKLCA6(li`|TG} z__x^EZI?vD@c%_VAc-FnB8mQi1xGV1jvNq;U;_6!@bL?wusk_V zlazuC$1YlJOKi(GN#O2K59@9&M1v3o+#;CT393j+^v3DNV2`NiUSaib5)!vZV<@@nJH{c ze72NckX|5Oc)LKfK+BVED|b^^IDWB5yT^X1dEv&gZ`QW0I)a0Q z&>h-kDr<_xGKd?5n~2A+g+&jb&qxpgw5#RMpqFcF`7FpDIV>nGcr4J?-dO36AO9xX z(>Q?H3wBP}J)GjszeoyUrcdH4h-yk|`r+|?zF20eZer`e>>y#^w_>qiv9vIErzEk6 zQ>NncNs%5LqOoSpZJEvt&5ZDj+{}YgZ*F7muiS*l5&{>ODT1eo`{#S3Cyb}3d(O)h zf=c87BuAtfTsB+~=P%?VWMd?Ad|P%t<2hC>x7r(vwcVHrIEfddulVx>J@_Nc3zkz> zQ|8u2ghq6%%O>7~jeTED*X?3#s+TvDq#8a>PR*(qH7{Q_?%7%Mb0nq@F_>qX-K5|p ze@po|;+j;K?8DT+tiZ&`vSR1E5@6L;)nBP$H(@jZu(aZya0Hr`Z!xN2(MG5yOpBK~ zso9m;l*N`MYH@0~&F?Or+923i*&NURwt2EPpHrLHYoJ(gICMO$pVO_9I9fWaIix*e z#TvH|Fb;>{AJE+LCnIEz?OB-#hbP(G}>3!NWIw5y1c5V{L5D;*G3^)tl&~N5x4ApIDUUoHZmUGtL zpIN^iSnAe2*4tS=GTe*auA1L?Cy|IbqS?iLB6W&%s@tTIyxyuBlec^Ba@}g z=f1qE*loGyIO`-jDO*{x{PzWi@_VU-Y#!a4qbdzk>r<)9)&;{Uj)o;`Q=nprLgs?E z1w_&mc^~qRL3me(AaGiE-O zF*TmHaNiQHr6n|(IpK`>a7FUk^?954-ajV1d54%lx2uC&UFnJLqBNS5`6KPe%)6&L zE=z|=jExV7iG0tLo2d&%BSs!)<@w7p4fNfKQVDhRBpu$9=VKpoVt(Y0=9}h|sKcx4 zJi3A!wpOkyhBfBv2YsezrhiTUp5~mrEicw9ZL7CG+Hk>~ovtviVAOn>{ccIX5Z9Do zvnaV#1e#MKR$6WxaGcziBcZLPu~mUpIbN}@^|;&S)E3nS+Fq?_HH^4x@73Qdeps|S zfuC2>H`P_Gk8P|m*qPJ~X&LqDy91gvtR4Se%Dk4maaQS5%LJYk70&}|-BjtdO3RLx z6Pr&4Z~Rv{XSipM=EfHEJs}7ciEodBYDsK~kp$-)gPn?wB}z#+Nz8~DiY4~VXKy%6 z*qzxZIqON@^8Wt4x#WCHRhW6rmddV9Jk8s@k9##2WBk;I7~ZceRGZLJ zNSWU3%d_pgn0(5*GX|T|K-kpZG-p5X^x3CW`(Q0|$yWfhy{zUp zzu~xnJw_{cgt-u@d)4B3uC-cMGkZ77xD?zv;mf+VeWYF7z+?Nv)@^B^?czK!Kwki) z>mF%evDrTJR1GJNTH;&x;NlnL)i)93kE%`5!{7X%aeI4**%KR?E|c*)Gl8GgM{RQJ z_cVwIb>-R~*Kzpb)I%Gn?)B{WRGQD& zeA0E$3fA$sR(5yor-+MV&_mwMn85I(9=*@^|tB{gkK7e^)#b&qKwR(BjTNBl=hee(I5t zV*)1_qNW;BW^!^6G~hNo1Qa9&1T?q>3I2x&vV?&7rwsu?3H}A=Bw0}Z>VfRdg8o+< ziu84`gn3943<6`RqT#F|C(C1OXTxApjnHiWE|Ig5zEzSNf z&|XLWp#8J2Kg;pGPR65X>27MRDQanBYU>0hO@N(^jqjgj{R9FV7^kqqu^+13TDzPD+HMN82`7vzwmsFuYCFszWq6re|o{EB7nfh_&+BsfG`lU z9{~X&1R*6Vtl|!N*ap{VD&as6ayt)m9HoZ*gh3i63hN(CUKmu>wP_(32tD_BZGK^W zaX0VV3%QtN5fcI>E51n@0FS{fV-e?sLLt}cSB|h$cf;)_{#$3{u(a)d|L}MpsHNL> zJpn9E9~~}>`}qmB9|DgQ+T;roHS~{u=s(RUNS1M20|RTFO0mDgSxijlGD3eJNSQl3 zlll!}k-%`Teb5vguM_<9gaM5d7J|ev@OM%$8t?zd&??{8*||8GnQf~r{*L;)#m*TP zCSKl_I<)BwJmdTT*5YQVO#5FEXses~%+hH$=N*zY!{-sq z*iD_urEv|oB9#zw&@8S4K z%=Idd^Ij+XgTio!>etJF$ZE%}LC2>n&Ny0NbF+^3fazBC5RjU)Oq1dsbNuih-Hr11 zXJtz2q&_P+Y#1Zn!e;cd^yr5^QBSDQ(MzDaW^KcbBdxLAC~$0pNkjG7jNQ`Lf2prl z3!umuGYkS{w;q&N#AY?umaQ~q^KINQ(we6=c?R6a4<#l|OcI9q8Kj57ucQS;<~#4;Pa13;M9ag~?HWXDU#`L0BU zqU)SPyqW1c2RdqUW$jiIhk1`eq9Lb@yV`R%zo(LFqdUi~o0J0}Bf$p?pibQ<*0yd` zflAVcVbfxh5XP4kMrz)Z)>znHBGenWbL0@J@8zW9@;98s17HiVEb6%%mA{eiJiAZko!i$8 zEuYP9VjY3!;<3aE-;KpBY3^@#S9ZRod}|I=RzlwUu_RPaEA*Vp&dN(~e&8@ND0uuf zX44?+&jBex%{dN`&jPjeuf`j@g&!uWXRWU5|ZoW4F z6h*VTKT7Gqg#{>QQ28AXZRy&1q^CJ9X|putZ#~~Mncr15aYi%&wCms$O=4Ib zIS-q!4hCPmA2+KlT`|}7z@kno-?pwr{s0 z|4Su25)vg-Kp8xX6MBR1(|5<2`JIpq%fq_a3`hFkz>PKAai`HXA-JffrK{Z*$wm3( z^@WvouakV^q>U>(&z5C&ghgo7IrSa(U-OQUEhO}AJ;!-IT;)H)RCZ!V7U}@1HdP=j z=M}G)@5^=gg{C7J+J6_`MM9!s3Xp}t=R@&N)pm_0Fm;WW-~|8L+S6baGIQF*DJNzy zGejSJ?>|(W0l6D{j6DciQv^6Pf{L51@kX)6LYL0ZizN6&tD;BJ?~hL6ODnouy72~Z zPYRwog$UaYqTLdjT*o_ak50TRNMpt3D;?^+A8Jn$klbQU+9SLbEXaQggwii#iDy4y4s$1AIDHU4vNj9>;tJxk)J z;+r37=spNvqdA0tUg>*1q<^p{15*Sixa;dEcQ)cV=-d-(x`mF)t4DRWIgpu{>Nt}y z*>ThDxZ4F6|8%|7LZ{!{SQfuS!u%5L)?}>t9&Zhn`@{WY=8N=$Gd_3v{pgKvQJVcD z^q_#3OhL1AaqS6XWCExG^gj80!1jUO%gp%@tJ*=Gl1TyGUw3qhi!0g7RSLPwnRz~U z9)j9l^SDRy1T-;h*-OuEq;S~^#Wsyxa-~NVT)RDQVf3yCk+ME92Rn_mWR+>R=+*F> zqcM`f%`ZNKaLZS{=f$WUrH{)$GN!A?nat$= zy6*sgLzA!=ouw1s4y9+I3|5dX6zSlH^Xnu9DOYpOs^NXzBCwU!=H9lIh;JFV>b9J{ zo{?1Z*l$pXwLWWi%pF-jzm*XD#4^m8=mk2mKrWD62mZ>%`K<3azqNILU^`A=MeO@C zaF9y=`7`%6&~0L}p|L19=+a^7Gi3=chW}}pex|bCX-ft5P0N^ramjE#m$SJ9efF?^3mp{S(d)=D})sXHQg`x`G|O|KAn8u!olK&?iZ&+P0uR^YdX&im_i@umx` zLZgnyX}bjF_Gfw@wvA5c>xslSpcjF@{hF{L(@`q^XHk#Qv)nCp^E3)BZ)=KM<9}F= z%L5(A6(7ngauZ{Xrb(=KalITXpvKVlDD7iH(UC?li+)5@ME_$5#u&-1V4Yji#0~DS z!;f3V_9yr8oLBIYb<^$dp(A!jBj0qBABn+XjzNJaQ?p}4j=@uo8)MVz`lVxHW{186 zg@&B{;X;ieJ5++axvlr?74(c=Tcy~TQrp$}L+>>N;_W*2FeQ>-gES_4SXgx2GpJMd zZv?~xw|8S>Nzw$zux$rQZoqkR;M)W|sjJWPq>v6*+&hCcL1NVnZG-4;e6^shQMb4t zaHzrAdUs?x2|L+$O3y|qjl#pA?^yJ*6(bnW^m6jaJ&qJ z_EotJ?y!O6M5XP+`_GC05CQQ@#08_$s#19FW}O|zInSH<_drO27w!(-K3APO=@rFE zC9Ji@l_!5bwJKqfQuj0+b5S8(Dz{``4dR(TkvpaK>k+%5r9uKW<)@=UeqkWvF&(5! zeHvv&UW@pi?@L2wK|b=LQ-4xhJ}EJ=cd;vbMHDni}ChDiLUEfaZVJ zdchdS+1sye@|E(@sE5_>BINv&y@!g37hSj4?%55!-D86vHSZwncXYh73zeJJK3xFp z+XM%ME20yDVh}awIw`hpy&DLniqc`?AQ+5_O^G%0)Zs-k7o6 zm#V-&!8PnqC#h|FlN#`*?WsdhQSbWqPQ0n(#vqxa?kO!*W?qFZw9~C_?~?1HSOuo< zHKINPYU@ecCJvYDIK-v(U4D2)rf14B8YW$ZrjzpBRZoQ54 z$rI2Ho~>TX6@yw7!_lWmZMm%3x`hLkK0X1SWqk?VHhYgUZILN9;1Mp*?;?v>g>#4!ZPRFn%obz45wN z6!d*#G9Bu(X}bN;S;GIDjLW@dRO;nIswv}+p^cl$xbm-c>^3(L4+s$ZR({H!@v(K1 z`PwyE8}Zw0`_cumgpAsCUiHB!uN7&F6mfW`01e!OI6#EjVvk9_M%3H79*0{|$S?;9FUNq7;kUZN z^y)jn;5~21X#2VGpn~^f?hv}0fQ~{jLo6LeMX?AIzta`>mhQ=2R{J4gM`soLn~n$f zbqe`8eE5Zb(m>k=q|3;O3$#HR^+1HM{dg*a#(7x8S^dgj0*!UPqFzTGI#Jm~9$n5d z+U}$d2Kk4A{l@y|Nz%$zTcPrNPjVyavp0f1-#UX2LfnU8W~u7FW{#Y{Yrw^|V>V?2 z=MV;>NT39N2eiV^ybqrPP}Wg1(97~kEJ|9NEP{4b(K%KW+Y}=_L#6P4cF4~Fbqg~~ zG@nZN|7EnHj1AoV9sJRgmU?t+@1r3!S6nDG0q;{Z6K(!PC%BZ*i|kD61q6>d-_lbF zDTTGq5zJk124r9phOMbvDs;4uTW6ac$B(Ni@wiR~Kl}O)oI7qUY++D0&`xV>!S?-0 z6+7|fY$XnQFl14dUF4b!mtq|EQt~k*Ha&vhxY9Td4EH*&M{_3h5!K}pwl2zQeu%W8 zOVQNQ=lPP=bEoVF&QGIP$Vrn3;&CsAmiivTcejsrh{5sx?4z5uj(l9E zu=mLxV32cEPmiHp-j~`vwj<6wOY=RDvVG^WXX|K1S7pX7F6e$MhrC)OjaZKk_3os~ zO5`rGa9Fyns4V`b2Dro(oQO2?B)1CxXDMpvMS3Rk%Cg1A#@4;5Ez>J(z!0s1#%I0j zto*G8KV##&h53UaulK7uE|b;O^70Z|@3_nO>xBbW_e<4_l!KT0N!1yuv~7XyO5Pm~ zO(s#CaZ73pv^e@LRXPo}cr`tSN1r_ZDHbFRzHVwQd{cd^rb9=Qq1DXBT7(W{cK*S*6`$H0OND2`8Rlmht0(0}+NlOIU+(n`Em-twlYZKA(2Pvbm4P z>00XG6H59=z)bvk0IJ4yV_(EMUBT^b>5$o=+(9z4ZQoK)@jsHlnd*N;jJvj1mZu{a z)57_3noBTvaoh&vy93Hx-h(zG^ga{2Ie%0KRTg`%7jniakLhWRHd!e(xu&<;hyxGS zoXZky9GkJZflL0)MrV{w^Jd0Pi|q$PlaabQ*Nc`_Ap4pkoetM}?`s@_&8hvI>Jxc_ z!30qFio80J;?I&H=}zd%v2NTgBfs*E<7nR*<1%_eT3=&1YNK>paQ zis(6?RKn0Q@eYB>%e~RmdcOblVTEWz zG39G!xLu-0I^w5kVv$Je+|sL~{Tmcq{LLCb?BiuEn+wZ8aI%!!#qIZ?pvfP zt0|lZq49hS9`X#l(=A>p;Ju9Z(?^1#k1->9HEP>fOgeVnAf@x|jQO6ekwRN)b)crl z;)&Gg-{RrL&(16;<1+ZJL(isy+vysd#7w>Bsx12bW~#O?$}SYQ3vOt3{(j|D`5X36jHPlVZ*%O|rC!H;aIGrhZ49PgW=}f%Akr z^u`n3dv=*kd$i57KJD@1YAdcrEsl1EuKpm+z7z4$y)(OJ%X{KMFjMn0Fd7GUIGj+e z>~DRI8VQMui6it>jj+kG&UJtNE4#+FD|ddlk?d%`pR7(#OY;2o7)4k8xcwze z2N9$Zv0z?6Nb3K&0$Q3cCex-?mZ^u*MtWT2U2uRFboSJFOOI#sRn7s&Ag{aw#YoP1 z?hmseO?8HNh^E*#+z{{iqIpc!js^G+q&o^4@!BHL-~9DCNYIEhOdKt&0zjcE>d z(svq3-9xQzI`#4a8=g(XAG;jK#O}9~i8kl&^UsIZFg&&ujZC{xwbpu*T`+#xADyvy z5Smj7z{=G3m z3N$MzGTR!T?6H(T!C>zo#3lkG39eI1ykL_; z{&|kVGcXvo{#T%wYYwiWU+P0*{Ix*I;G+02?0->N{>!HSio}04=WjasUn}wdYKv$v zu!~0u8ECX>?&nZ;&&rBvo6W3krX}Ilx*wl2@?NCMn9`WFgk2l1q?wa)b0K?@CPQOU z#|7$)(G$u%G@Xnst+<*8WVUQvT6Rgr4&T0I_% zi7G+HcUX;FxGV`B{UNY-Hp;h65o&}G04-sLskZ_l-i~i`UvSXTHHQX)WQRhHcM@Up zvB3s!*Flhq4!QUUb>awuEpW!hJ=IVrOryWE5`1k+IP;9###w{UhJoz+H9dUwMoKzPrRt>CO2pgdlFzerbT54R$Y(7%4C27D@QB9wDzck}t0H9Zm zqvR7Em*h~iZ_%@JCV-g|h15$HzNmoOO_GUtJ5YEr(e4_f%52Z7!5%OEW=1}~wKQT% zyxuq!2`Lurj}4C#Cp@w7Ho^+|*dZ^Woks}4Z^0;91Z`_Jq&jp*0Vwx|t-ez*jQ5t zQiX`WDUU1DHKgDv2RrYnwq&>P(&irL$=9lp1*XrmP_Y{@8Pf$g)WNM*$gj&;xyp|KIU!*K?WBH8>H9S!B*rT2#eY47|nnhXEEMulc7n8UxiK(H$Nt^*J}k6REDC)sF^eWHeqgk{^q>%tq*UpA=Gc^a2 zy@!zkPbP^K&@3S&&C8uwJH5VBZ5z8oIhue`5l>DHk8K5k6>xUOm?)HX1i^Unu1*mN zLOoO+yU%k(zSnkyT_U}PlbJ>SpxUPA>MrG_UGFH$XVHtX*2s{;Z~+^2SQb##li83$<9ruy?e1q`jT5X6 zeD5~v7SqwVv8<`jH$o45IbJYD*<4=rHyguuDGd88SH%qaV@jpY*B z5h1QGK*esGdsWp!R;dP;tUWCmY=1hxbtOR&C*D$gNdR}K^PDHs?-^u}j!KCxi@6Q2 zyjp@Q(X}5}UWU9=vkPh%IUX)9c&E^dWX^rKMIq@XuAT;w5nRI$HVG3%{>5ey#A|fa zwZ<+w#$~hpgoS1??a*x9(N(E_BNN>USF|Rmvc5;Z`Itec$jaiRRtHrR{aFCYkl^%5!f70NHLYB)bhGruW<1azR zTd5yLE@U@i|0FP@(0)=6IpBzG=P_kr(f}I^xE6r;VG^MR;Cs*pT~TnZ7onYMA+ko) zrE#^hgu%i&yM%${1f60@3Qx~hfiUUq_Pbfak%%#FSX4D98Q6DFukRQcGKP=z1zWWr zr#Vj%O1gahzQdu3vmHgzPotwR%$seVGV*CNZ_#C-cBxidHI+dRwv@)m(q`V%oEVzP z1aV?HfI@q?pYrM8p0Uv-C&}

_w`+YLkm)X|qjCkt(rI05G4RiX0mHHPu_?wXigmfA`)cl39>tc6dTW4n_uv0BZXSllHZD|BoT!^Le$c{fdr! zou_cl2j{hD-s3jRS;F+7@r?aal<(p=6?=>OcBAYPA}P>}%~aPisUh?%m5R6;wTguK znZ||?5*h=%wLf8d&ZDBr`qniTJ$s==RDV(W{iK;LNt~KCGE0RObFVpo)>lzmUgOd> zz2d*y^8M5hzdL*&zWAIbWKQZ#K;du9CxVc!lWRkg{^8jnfIF`xR27j+?dNZ-33V@> zKx{l*1~4PLmpC^rk=}-xh)4!&UE@0D{^}*uW_YtyDID%gH0gU}&%L%!7**=2>Cw+G zHg|2B2-m7OTMGDcGQg=Fd6&0wsy=HDV15~au%@;H!mA#zV#XTI3UJ`mW(7h%n)_v2 z;BtI#uMB!Co#2n@c!+aUiKmiDz`|q#>rUg+?`0m=PF9ta(k4sR?Q*eHoHD-)sR2hD zqp4j`v$aDg=QT24n0T>r_ZfZj7eGETuaHWf(>-g3&Z7X z$kyTAzmg*wCG}Av;kk2WIB7LWL6HTJ4+3M@_2_(-j7nhx;u+*gIBQ#Q8TJe;Ht{!f zpUEG4Yp=|f-mZp0BlQ>Ml}G975NsyAI+6@&qU&e8Y&TznX=u8I>j~M?0&Wb~JYF{8 z&lVg%`Pfu@#&>58k4TY)w6lq7AhCUzjW0?qt| zo|y-7<;s9QIJ)Dn#;FzudzzO>yNT2hiW zG2nVN1m^nVm`qtXg;CaXnFGJYp)f2nrYH$a`=vh3)Ng2H?v+3>O>@*j!Bt}wx0+VF zSZ0ovZx{vo_Eq`zAwz^U*{}^4K&IG8*$-oi1!E1zm2*fKi?w4k(8K?VI9n(KZ#~LEAR%k|3`8!XvdDz#JsJ_@$ zX1MsmWPymSR|U!YignH61xxb@s*&45!$O;6yY(z>@(NRV;|y82y5p_R1*^RxH1AAl zBZ6JKP<_=$b~DL(yqY?>!u3Xd+qo`|^lh1LMV#sPGyN$@NUs$ovXgZ5`vT370^Dfx zJRVbl8Ea#UZ!ilf)@{)zwJOwXIViMKdc#xpfW`BIr1JIqFy#tvS3X4hVj@g_b-y-U zfT#f*=#>6_^mH#0VDcACn3;2PM+%e~Vc234ps4N2YI1X7;?$ZAwoEN7g*){9k}6i< zxNX06wExLtl3wN0sUvKT zcJp1rkSOi@A!#NHiiDsfj(SY7X+DzQSd>v&A;Mwvne)=TS>+T{pFXFKkJRZIs~Gpi zBlKq*w=rxFJ_T^-A>+L9XQr&+$~o9&-gMM(*_m4Yx)Sy?73ha7#%2niN$rY^{n_~^ zPOTRSwg}a09Pq6NBGXt$>C)!NDZ<7#Q;JH+*MxOu z=b=P@O?YZ;jgUPjJWm1rkj`AhJsJ>x{t40~oEEiy2VWhumT{k!OFq9|@TK?59gerD z4lX-4+jhX9yM{6j_;wam!22T8DUF4(rN6Ti*PxfHl8D9CjZG%Rn7Lm_duw;t1xY$- zFOMr~k*p0GTKGp$LCIX@r`%})bD$1?nGr`;N=i6eM~WF0HXxQ-bQIssOVK187r~^j zoCU&K{G0To$?lB&^T5K!n0Iy6c%^3Qmh)p0FJ--!LsvP$#}U=**Ni3WmD$dHVbkrU>UsV&ahZ+eh3ABXT|RU_J~KLAr3J?f)veOp$Wcb47}%_8WES9+ zU#*wO3K3fhSv$;uPcsCoX^iC~mu3=9jV*(8sbF7+Y7F5ZlqqjBj8d=aOh`20A4=dm z7`2wFo1_NfQ%LW$nL<@UGwl{4_(HD9Ne9kcS6_AUkpaIjFu~B^D8WU;Y;v*c z5u}$Cm)c+O2IVDDyY=hkgL1f?{fh`tN1ZOpmtRxdipy-~ayltD+_g)`ndQKiVElFU zdt&@#zXZ)%q;3}c_e9GD$W1>Rgi(4$i_fzA$jR}SP4YQJD zcBaWllm$l`wkNSP$)MD;hvEx=*|GKl)+5-x(`T8#2)dx$i21PaDLHvv=+|=DRt>hDSfaejfqH;*~dJyR9{Rxu&?7$q3aLL@4Phg~NR* zHb~`BLRPWmoaaPC87xO~HDp|$fmUFPFkap$urURQAR4niPGjG93a=*BjXl7(y?qzc zzaPY{oJzSZ#tpq2KuxP&9VHnP+oE*(I|bR3NE)AMYmLAL=cb& zg}GX5@j%^&M&fd%QKe;QoVTmEiuDpEKdr)W%OPEiKp$ z_?5!2u)`3ny^tqqXU*H((u7~X!4qc6g_R$;qd-e}F&zq1(rGOjt7H@QQP~$4x?l9D z1`%qbcpj&&ekEw?2(S~@BD%a{BRV)5o%CB>5l;;tfsAZJb=n=St`k?mZrrCN05n6w zY%!W7ag;)QZq8co0cL*+mY;(W2X+Ur|4$&5ds(Kp^Xc7(J!F_siX z`}(hLPnw1CL+x|Gv!#^uFRP8`t6taJR_)X5C4z0f$!Hsrt3aN7{?o`Zhx}H9R)>j( zg8^(_rIk92H z;`rs=9W(=#OH^kctFK(nNL@mCgTj~vPdiHaX5BZu4jm%4{d#SvZS*cbd}L2`we1qQ zQE)D8<}3?cu1^U?6G@M#UtSo2#lvNS9>#@hb?V(s*}`!o?Ww}jM5TGm9unLgwux6A zvQ7bE$?2P5p_lNB9#jh?rbg`TlWr?&*C~#KdEF&y7ZgKcOsach!gTG(XhalKSgrCH z@zMiegf2*6qmWTq_w{>fiBs7Yi#30~#p4Ps8pNKZQn_Igxiv6TJs!?wQe-1p#X-c= zfCrl|hOcdGG$hfEq6-sNN66^-zw65ec%oZGhj!013g+y!oAXQFswt_c@w%@!S{cV& z2@Un-iEGx}Uk0Sw#kpiX2MLT_F8?l;=AG>Z8{TE>}N3@=trHF{>JuGz(f! zA{0<3Vq9phKyuIm9QU22tIhXNOsnN=vxNw}^ZTEnV4S%N?xb*?Q`@B&I9u5*xSBKa>EFrGpu|`z zpG*}Wg*j2b@uzwdan*DRqRF*!DVIU-viaU|gtv-v^?Dc3(BuGh3sD=j6n5!t+&7xW zszFW9$v_)p_V27m+$J|XG+ryEfUCNNiVwmG-oz*Gt-Mg_q5tgILwamWLAsl zVX(qJwubB$SC29{?rIvj zN?D=S;w&^v05v~_3QpK@8V@BQ+R2^zurVfon@RSiRL@y_?HOvGk{Rdj#KYX{x74-H z0OZ6PH-18WHv`M?_K5s74lF!uY}E^SU41nJaB(sT9NK{m6?60V;T$vE3|EbrfU@^K zq5~#l;%u}7Heo@8&E?l!D3tdp$kyGyz~(j6715T>G6#h*e1A@Ei3}$mJp=u_Y1i4( zXEWpTv9zOygswCZ}>A5F?4D&Xz`Dt+63zZ|yl}F9kV(}EXA^^dat&a7AeetC?S=|taGDQgZ48%}aa?^pR0*OdUFRcOjliSXgXGSmjir`V); zy(ryOf(OXK5lS!45ZkaLx@QuOH;z3F8-Pp`W4urIyI23Eskc}&ms=&e+)#i$QVyt1 zf5B>P%7ZPFJ%z^=x_8WOD~`_!M{Dw@C& zeP{6wtGUQ&u?BZjCh3#QM13T!dea^ktZa<_ntp?=yFb`4);3MQdDEj)l6f{gCdId_ z8x3S92Vl{=jWTt+9lgt52P`sATj94qk03FawfKs zTw@%u3FZ}J<_ysln1KOllFF;gGy7b zau8LOG4kis5bK_XTV-PJVfF%p${+txsW-(F*KRi@i;8xvweD3$cP3+;V7~l@M0nJz z=slhf^<8!rR`;5@M~UD|Rxr>wNoUh6Wn&NpgYd?4?#B=r25I+Lk)g3`&DQtF&W`2m z{l+OexNCA2|$&rQBb&S1^Hkz_O zg+JvQSyM#%6M96@S~kO*PTJH#?V8!W4OT)Ub>ot)=3vkK)#kmfWjuIleg7qGxNO*cyP}gnp8>9aP3{Z?^u|;Uxl?JvUe++k|5erR@*a7s$33ivYdn$ zpUJoULjlxDl!1``#LB1Q&Z0zt0jsyT^ABsTy68vL{rC<`RoI!+tR;Jz#$4+zKyRxG zm>F1Om#B+9l_#=|#+jlY))`X?8zQzt1C7k7BkQzZT)$a(*8y#5%YKPdh6tuJZ8(Qh zNBawdzXs$gS`s#AgFK^yu}%WHhlKV0#u_rn!VFDF)OZ&tK=cV1!0?jA+MxPfK3%c* zi4jZAul_JLU_D>o-KlA<%vIW{BB))I8H&nUQDF$*<=vGSJ!!A=f*PL8pla)dWf^nMqwuh-%$@*9;R9mC5REFbZshtC^Vc z4!D#P12*i!#|dXVrre0t$QzxTy%L)ga%9dTtX6Yb;YMoQ(J2|s$-tB%?(v}`r%;&yHdF=qs-x^=@H@B6r->gHDbO)8z6xDcR z@;rDEig~cv#+HgEA5$Ls8wX1k`jA^xC@3b`297|)=?{5X?jhhW47{Jix9;^bf%683 zWqY&u$S?QHb>ffjN0AALN{l%+o2`rmwn-#rcvY5mnr*+hR>mP69XMM5 zulByOEv}{87IzKqmH+{QySoGr1b26LcXxLP?(PySxO;GScZa*!?>^5t|KL7X`djzv z?zL*x)KRlWxfoHLG6x^(;(Li?de-yWb2=z4-ZCIj{FA5xj`ocg_gC<%R9|h(ph#8M@TgPoGSe|b(5_r zMl=J$r|f$+D?tkMQi~GAQ6Hs9q4^!i)kkp6Q+m9EG{LXW+_{;89d8RoiQ#A_QNh)gS~V@$1bpURd8dus&4Y-goPmf^ZEJ46OJEezunM2el`FKwd}EL)JGjIt z4@F!xh^}-j$*rDtB|O#ALCJh=)-5u1upRpoYqx9m3-Q@>0eAU2S$bw>dMA>NfGCdK zjY{2^9?OeWwSfR6=*^S|0KOmxfF#9l*|5nu_*+@X3503HTfqqwfC9?ZA6mwLbC(Y^ zKnsg9x*K%y@#ULi;dTgWx8@9l^BBQgCI2e8UfX}Cq2I2fu@57aX?@k24=MJbOEj8O zS4^-(rPS%cPSv1Jc5X1XfJ5D|L{MZ- z80}x|>+gp-yT3yMoXhWI`vW`xiecS9NxVBsM76UVsN34J?G-?{=Z#} z{G{@dQ~BqkqfL$ce$z;*DGQak5v$?8;>uRbgN)k9W$HYce3rBF7$ba%jL~Mwu=yG|K(g^yW4$#@{sB| z-zW(3S^t-%XCbofG~EO16f)DCV@MdhW;c+@!21Ax7qU9Oe>Zm*J84sAmsXNMHFlqp z+#>PszK<{uKBUhSfx2O4C0Q{Bd5)P8x>BaUI&1 z@>-HPx_YDQIcM{i49fFV5F+wHXjoFM)5j*NS@`Z;RwUXJ$jJUMR7dI+ba+G zlcA+FJn`jC|GcGt^_7j7Iyem0zI)2l;#*j$-RWJBqLn@YNjhVBFI@$}!c$K8ZFP)q zU@xH1J&Vd@mx|Nj368>|(`NM=(5eyDKMXC?jbaQ_5k5mEmJe92PTyG2ILMvf7duf< z)%KZTvj5=9)m!F{`ZkcXX3yEo9hIHP-jP=mqjF85z;W;y%xW z(1@9jNyTJJJqK7-7F2k*Am@;n>x0oji=cRK8m|?rMjP^XoS2l-0fu$LY!Kn{ipSrI zy$8M4gxjZuw%}P=)2T&6{8XJ3w7m2REI8~&Rxesqp!Cac582dl`^I`4MJ?i5uKM)? zl`+Ui6{JHEIVCbCuq0YXFe0gkmkPJzRj9`~;~LIT(s1|~WhCmmNJE!|XQd&;^~=bq zQo#^X(%&xFtaZNFdR$)rFKljcFr-`S1PiJ9n3n(<`OyLckN}4+VH~QFc~wm@f0WI) zY5KPc(o0a}TC?7X6==mlC(S>D1Q-!O<_&bZKJ>$7KVq6?%qIrTl+*z4GkuojqWEB8 zxvK88ZzUKz)E`QM3Pum3b*R95nJN(BjeIXT@Da4lT2J z7*z)VO?li=9!{;)G%tVa0Vy^!w~PlZVxpGoORRj+AR}B=`O=N*Rno9sY+28s=dUyr zF?Df9E`*U;>eKiLrTR`)cng8_fwTS>(wXl4x|3U|#1La%&!aG9v_nJMoMy`WNgCXa zGQdiE#;Tb?a=;O-At-ft{!oiqgHN!=fpN6arsh9RCfSyvR-~ixz`hvXgl*Ysd0>{}8teKv z-OhvW>|g=6m(J%W!W2-RJ;f{9Rxs>2XwQx>w`K+D>kSrYyzSl{1o2)}O|al1$FE5D zvT0`bzc$?dqMK**{o?v5+u+ZOL@I5BC$dEht zmy;@!Zfn79icG}TB|>j1_yR_(gXtCqBe`O6S2$;kJUn@8*)P|qO1x?CcXz(oN|x5Z z-moM=Djz-9;3jE2^_gQW^A(|5QI@J@ow18TEqerFNNGIqX{1#mDrmQhy=FgDrY{gc z4nzD=pzc`V)F`BVC0*bz+`=?uRE6MC9f1k#qh>Ti1g| z{Yox5aZ>MXkP-!)xk8m0nv?du&W+yPO6e28j82B%K)n|)|`ubH$~Oo=e7 z{@2KnLGhS5mkQ2Trv;@kmTTWci2j6cTF(8Z7|OT}s~-N+H^wU~S#v~eO)<9}B*HCfhgE!SU5MLH&jAQybVp~4InA|!SgsW5y?zXt)XuS1hBR9`1sw@?C z=?+-4HB$HYPh0%yVp(sDtA8d}&sl|sqd$&&NqRMDRm^Knp$}i7(W_T@eE{Wl$1YGS zDi1$q!>NP?pfQCU#(U5hbTY}}^QO%bB0ZEhg%}$1B15YA zidGJVDFK4IlL5KOAhR-Ef4{d^qSeZ8xJglzihtRC5SFu-z>%>Z6g+x`DApR%)|=c= zYx`1ATHmmOjH+j3TS+kWYG`8sNHS-zLpCQ(CiGmh2;=N_0%Nk!@h9m#{Be z^q}I+o7i-$iH`Y~FE*fo+>R4mGU$x>6^{%&)?XwDwbsU$7C$l1BrGTjd$8f_*D&jP zKOmv>jh0xzl=V_*k>efvZo~D!t4IxvN^cpcGEnjBTtNFJ!fsCA{jk#f6lZNgyk{3s=2%g`;-+c_Q+zcfZno0Ycr>A z@94u2RCA^Us`Z2|UmtwnAM)qI%uL>u@)0V_AiyN||R&;gbrADo?Uw?r0oCzQ(U))a>bI+Owra9`6rG_fS2HhMBN0yW! zy6ykoE6z)kAb3bw56syGGYHSNscqs(B>>cJ@CQMs+D5 z4#1|Xagmb?UI)#snnylwSh8z@{GxX>w=dJ}L;f}s09o6w{s~{1z)mBx`{h&lPEiZm z!=|PkW13!`5f4U~fldg*h2jy){cwnyencs_|{ed==%^r{=g_k9^SA)c(6soN-?10QUCeUGZO@nypsu2E`RF}(p>5Cn#?MIfnJ=xx1doxkT+}Im;U&40 zJiT;CPM*tVCn+kvt`B|(y;Sy>S{NQXed4F>CApTzz~2ueox8Cd!5u0=D}||FgC2`>3iZ1YR`&w*FQ-IPGsP4@)_D2kj$HBxjNHGiA7vewsb+SZl z-S@`rZ28-8I>>7@lnX8-?$zz~j;4+oCWT7$v-h)Bsp_DfaR_Uk#C%k^-NJpA`O5w# zontyb%R0sY1%yn$8KZ?of0r_Pb}fnMh@4(f=qrg6M;)8N05Z(1{g&|;p!M>DfeF9g zD+g;F#12WCmRTVdSjajcrS^^btBx1B5(KhXP5kjNoL3%WnAhCD(#=%_tUMW1`x3h| zzUgtWLaA?1s4mCYXPcEELX8%Fb*k+TCNpdiGH<2{VzZ^B2^7){lzbkUh=ogP35~7GA zpO9IzQ3>v}A~?8dOo!DB;%5a+*cDx*Z&CV8lCn3?^uTK9zY`(4pJhZlXna8-Qj09f z9n+GtS|PpaASF9zsec5gn?5~;$(OjMS=!&#k1o%p)a*Od-b^j0^yMWhMdP{Yzti;?Q_HpcpXY0@3CR zAXCo24Z;gim8^VD2E=lL8Nc=-;|+C=`ewCwXZHFW#S!a$AMpKVq%>t*o)z)~2s#J- zNWlcq@gEbf+-YUMtG&lBhrpe$G-c&T z1btP{Tho_~sFZ={6^WIhzNlWw(!WF%gs`YFV^j3FZU3OrHB_)w8ja&_Ro0%Vy(L>N+5hP>1Xz>fdFx?dfmaHfZ&V%%I1YW1yenQfsz%m;H|)` zptp(eX{)?xlj<*=BWZPa3^q0CAveW&qe0MdpjGGJX7fw8z=$ukXw@jSY_WbOm_L~C zCtT{amr9{Qm@d8tC^1v&@qaf^)|IOx*w{vvW1v_v3*dd;ajrOmA7qfT)r7r!g9hpI zM|u+!vBaglQB-bNBDQFCF8(cisYuict6g5WAq4tbSmw7R(4XJoM>`iB+3@p@oO6m! z-GMr2GbJ`E)ek)66A)_Ifyn@07szw8CT$6c8gwO=#9K@;5~o$SEW&%#*F->2kh8$eT z2uP!WHot)1j!X!WjCR_5Of{p>sv`-=wJC#k86$(3hzgtyQ)3PeQKl|SSglF}w1SN$ zDGbI2QhUkB-wo}`AO~LSc{lc=HxE;e7IoOnZ<;oXe?9*6?Ea*mYL<3E?g6{5_ftv# z45XwPZYkYdroAf=^}oTS?JK$Xq=Xii!Z^1CND-o-JQIR)h^@Iq9Vb|<~ zS3l7dcBB*VsimO2<}lL^=)pE6Wbw{j>~pn9S2M(H^ht^zi}d>BLJ<|Jt!jawK|K$R z@hpU18HSOS2~5n{4OH}Gh0!fPXxjc!)Rx0*9R?UbBD1-L*H0bR!T*`a zD+()-aSI!5jgf~VnOs>w#Tl}$a_&>Ygdr{16o8{N3P8F;iWZGn?dr4m`=z137+_e8 zjz5)_M|L@WISl8shxuewC;fL1*B{ET6{Rw}S?FiKA~GI0Jc2kSi9Wl7x}{c$p7BiY z9LFdxzB2b;n3Sy{pE1xDPtI!e!7hHBjR_ejINoC@7`Bbj~x5LLuzRf(~^EhM(jyti*9s4YZKS^#t^@! zv-flNLe6vb>*DxJLkh0z?%qbeYImTd6#UqN@awc@xur8LatQYuf=Cz+LHW+6p(WobI%>9B{rmEXJr`sdp4VUa+>P z%B}N#Twbewa(I;2UjZ$vb4Pr+kTi3qVHRu{t~5XP|Mri5U4d^Yyp?#uzn*32p)R}n zvpQexK-4oc)a^>TH4Cv)ngkqTdw7hsr6b_dNk zR4IsornN@o{Kj`W#M)MKsBg9}|z_k1OXyi*XSh$YBO{L9; z32`B{DA~{_3$+@FnAD>xr<9L^4BNe6lB5dPY2wvqm14r774K=o%f}Q5H_1-hEYqvS zS6i{;fkYV_4GGc+L3XSmopv?QLw-fqAVV*OZpa%?z{5oo=~KmG?uEyBn9_Gj9~k*- z0kCs?^hVvSfoF8~^7Gj2TYNS@n3_l4JGeJGl0Rjq5d)Fqpq6M0tw=1gBR=;-LOR>i zRdJ>fq36IIgNIHnC+!JfLpn%2#*@j(v-itN`C&UcF*KwT@($L~Ajl@FSlQ~gkEZ9r z_$M$C_u%ssn!-;Gay{vfZX@t*ALnsw&6zft-9bF(Y*CPuTXagn$RW23W;dIhc~oR6CM0^_y!lBiONOurr~XSa21o2EI0IxzEyo-BCYiJ5qj1OO zRzr_zYcE(l)D+<{=~bpOy?NrCisgY-$`=JfL&U^^tZYXdM^@;A!#8ST4{MtIG#a`7eiRE%8W%2xgUBP9dBQxhVw*J3m#_P=h<=4#YPD`Ue|PIKtBk)QuqDg4660R?P% z#*Zebyr?u#5KI2^!0~JemrXkow#u&ni%*SF3lmahf-2$!3L`4T_qN^X6@7_h(@;{k zIZm^wcD>t0CU{Y&(PPU7?m$U#X8;5W_u7dT3hWSYM+C*i==$3(0_YRD2({?mxnV!w zhyubn3urTz7~7J%A7Z2646zH2Fo^gMW4$0{K+`lpdCW{l&)py#0K1PrNzKNP!7sKC z!NX9nm6AQP5B@!;N$H?JWH>C8u=^TT6V9=!m+EMZUWAdJ{$s$6Lj7kl1`)P8$T-m2 zJO@t=-5I}p>(1;Q7>^d#w&XPE`dp_OT~DQ^3JKe0ej z_J~PWh)Gzr(Yy37(a?uu-9cGww&67UP6edG zHcdN`##935YTP1%L`>8OI$HY0RSG#3rzuGY-X{Ct?<&Uph+9FIg;Nc&)dZ@Jy!H57o|R8&UV;c zbMYW?yT&e;LLyUd$+9}7?nfuor4p-L_!&JlUsgfh|K|+UPmM5QsGMsuN1t7~sUgj3 znJB$mTA^A3DySdu!jpG{gReDzqy_z&&K&{eY)<$SDu4*SRBP-Iii&x*Zm=ul!z*8o?@#|rwDQrIAR+VjUkYckk74}b%9f<~4?mFv zU;`8!y2$_GDv9`LPeVQTpw0Xb%?LU0Ul}_AJpa;S=mB3AYrk%B_z#Ur%7@{`q22w< zY4d;Qjgul=PBos4uvO}eq#v)6J2hC8D%5_;ux|!X-QyW&xf(t0(s`@45xs0cClffY z)C|wyu0PE;4Pz*4$OjWX4vHleNG2<}s%=wiKYnl748n7Oc}Oumu^quQn@twQo{R7FS9f>q{rKbkX*T254 z^7gD_{Z!8N>iI-Q_}4v;Dz<$|WOX3;ae_Jg3<8T|VD+dY?NwAW1@ zS1rV{-Bu_?l5+bNM&?^fK;1ZJS^GmD9c}0Pb0=Ri+y~VVHVKd$k*yr6t6d$nI{!FO9CD?Jw}li@bD`wQ9A1aoBl{dFBH z!z|KIA?^JF`CX3paY#04MwYL`10as2eZRAQSIT<$LlG1=KTwQbx~11^ zyBNb1NfrTIzQ0@Td%w>Q;b@-)YjccvO!^bfwzTXoufI7d(Ve>taB=!pF;V(yQ~?{Q zv)>mFqtYcfGtmJibZi1}b!W-7gM-#rI&9U&$Ng+=tgvIp{VaE@#}i||GSBy0?f0Kr z*J%q0ec@QjnI<>qD1DCo8FPVJyko8RYwpR$yw|C~D0j6Tu&=pKO=aQd)V9Z`J*2PX zqumq2q3C-DXqpI}vb6+WGTlacS z1V`qs#&=$^P49@ubl&bEd^WD?xSRg6K}2d7FW3FmZbj4MVKX@S84x)dJC?SGt-!q3 zn(KaI+v=&+F5!LifYH0{)IRjPOZRH09eCW6=T58`W{W^6zT3esg-$w8_rYeFmznIh zdL8fWO8pgq2(D0TiACHut0C^$4gw~6BIhd@o?9!1dR0tXa5|T6_@m$LfKn8|yY6$X zt3FqIcLLfeqnU$`ZTw~Or)EnM+fP|5rxMPT^PPCvO2!l489F=9x}z>=j-Si&u?oK& z@v+&O06n0~PCQZIv-Uhzx2{y3OP^@_QDfC|XnOZyX_}Jn@88O$-*g#oWz<$!GHw?t zHMzpu0-v4V1)=Q+$*@|Zv|qNc>cj{f6$e;tc8HMb0DkJK<4Do>O2w7P^n1_0c~Ff8 z+q#?$zb|^=jdv676^RHhe4s~r2EMH(wl=&-J4R&s8xngDOlD0m(wQ_B_+PiE=viig zx{_Df>$vset>r)|Yq=U0y+2NP1KUq0JkRXQXpsi-*sL-p6Gw}V1~V(`>@2H>A821M zg84G4V-;En2=H(0520ls0xaeCnCRcu&R?~RBVXUEY9olJkX7Oiolop9h8w5)y<+g< z3Zcd)1tL9{)}wLoqB{&;TbArj-HvDccRUlpgLT0&k!#BReLMs92oCroP5t3 zEy_cPxE|UA8*p1_TbtwP8j7TM6d8QG!b0t~P;!L%D_A>-qe1>q1cO2$ZH^) zAi0y|rR06Pj6O%67?u{8`v!o9GHFaq0kjWKb$ritC8_D{C%>4Kks=t7kBWG2OOB4$Usu=1 z_B?Sp?Mj>c$=IZ&A&j`Q;a>ROulce?8nGK&&Y=4rH>$R=1G|}GI+0B#j8n1dm#4cQ zHE3#BCw#cWRgeW`y{_W9hR?mfv< zXw1M`z&Nx>!AG!LdTvLuDY3lxi&l2exA%Mr+rvB(0#xml9Hzb%_}BoLUly%4+C8K zGcpZ=I7OJd{h3i#qjOcZW=PP^EvotB_U5?P7})lrv=jQ{hu@JtvuCDB^3jua$+(wI ze1rpM_9N)OU86;P*_RQ6hEWRYwd;3r759HvBmjzHO_e`H_m(J7rdA1Q=fqSH*_v_x8;FGVNEh$ISJ|%&|3j z*TaJ3qs(gO)|t$k%#!A3d-o*e-&cuGln8{uu4~s>Y;zcoMB3+f8Jz3|3Nqm`p7$r9 z(D;*6UUzlbtFc0Z=cKDE*9Fb+4~MeeZYk2U-WSl`lZWvZiiYhrBgeoCtSzvhr=+aOYspQL^&{U^d12-<-T<^ zb6I@wei`sCCM4wG2kF~}dW?mGVCQy2F*Gs~T^C35@_4(>UVy%0h~mc*VH~eDnc92j z3C42W_E&$utTl&Fg;3^(PDTFX8>$oO&*IQ{vvpmadj8~#?6Cpy*`qHH4QA=VV*{Kh z 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 - AWS Lambda with Custom Layers -

- -Components: -- Python Lambda function -- Custom Lambda layer with dependencies -- SAM CLI for local building and execution - ---- - -## Project Structure -``` -├── lambda-sam-layers _# folder containing necessary code and template for Lambda Layers implementation_ -│ ├── custom-lambda-layer _# folder containing python dependencies (requirements.txt)_ -│ ├── events _# folder containing json files for Lambda Layers input events_ -│ ├── img/lambda-sam-layers.png _# Architecture diagram_ -│ ├── lambda_layers_src _# folder containing source code for Lambda Layers function_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ -``` - ---- - -## Prerequisites -- AWS SAM CLI -- Python 3.9 -- pip package manager -- Docker -- zip utilities - ---- - -## Build and Test Process - -1. Navigate to project directory: -```sh -cd lambda-sam-layers -``` - -2. Build the Lambda layer: -```sh -sam build LambdaLayersLayer \ - --use-container \ - --build-image amazon/aws-sam-cli-build-image-python3.9 -``` - -3. Test the function with layer: -```sh -sam local invoke LambdaLayersFunction \ - --event events/lambda-layers-event.json -``` - -Expected output will include: -``` -Version of requests library: 2.31.0 -... -[Function response with GitHub API endpoints] -``` - ---- - -## Understanding Lambda Layers - -Lambda layers provide: -- Shared code and dependencies across functions -- Reduced deployment package size -- Simplified dependency management -- Consistent runtime environment - -Layer structure: -``` -python/ # Python runtime directory - └── lib/ # Python packages - └── python3.9/ - └── site-packages/ - └── [dependencies] -``` - -Build process: -1. Resolves layer dependencies -2. Packages dependencies in correct structure -3. Makes layer available to local Lambda function - ---- - -## Additional Resources -- [SAM CLI Layer Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-layers.html) -- [AWS Lambda Layers Guide](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) -- [SAM Local Testing Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) - -[Top](#contents) - diff --git a/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt b/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt deleted file mode 100755 index 2c24336e..00000000 --- a/local-test-samples/lambda-sam-layers/custom-lambda-layer/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.31.0 diff --git a/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json b/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json deleted file mode 100755 index 4ccb6ff4..00000000 --- a/local-test-samples/lambda-sam-layers/events/lambda-layers-event.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "key1": "value1", - "key2": "value2", - "key3": "value3" -} \ No newline at end of file diff --git a/local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png b/local-test-samples/lambda-sam-layers/img/lambda-sam-layers.png deleted file mode 100755 index 05b57993a8e1f88b4be3226c480c5ea6eb5e7a7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68083 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 - AWS Lambda with Custom Layers -

- -Components: -- Simple Step Functions state machine -- Local Step Functions endpoint -- Basic Pass state workflow - ---- - -## Project Structure -``` -├── stepfunctions-helloworld _# folder containing environment variables file for Hello World Step Functions_ -│ ├── img/stepfunctions-helloworld.png _# Architecture diagram_ -│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ -│ └── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS CLI v2 -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - ---- - -## Local Setup - -1. Navigate to project directory: -```sh -cd stepfunctions-helloworld -``` - -2. Start Step Functions locally: -```sh -docker run -d --network host \ - --name stepfunctions -p 8083:8083 \ - --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local -``` - - -3. Configure environment: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='eu-west-1' -``` - ---- - -## Testing Process - -### 1. Create State Machine -```sh -aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ - --definition "{ \ - \"Comment\": \"Hello World State Machine of the Amazon States Language using a Pass state\",\ - \"StartAt\": \"HelloWorld\", \ - \"States\": { \ - \"HelloWorld\": { \ - \"Type\": \"Pass\", \ - \"End\": true \ - } \ - }}" --name "HelloWorld" --role-arn "arn:aws:iam::012345678901:role/DummyRole" -``` - - -### 2. Start Execution -```sh -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn "arn:aws:states:eu-west-1:123456789012:stateMachine:HelloWorld" -``` - -### 3. Check Execution Status -```sh -aws stepfunctions describe-execution \ - --endpoint http://localhost:8083 \ - --execution-arn "" -``` - -Expected output: -```json -{ - "status": "SUCCEEDED", - "startDate": "2024-12-21T14:01:55.670000+00:00", - "stopDate": "2024-12-21T14:01:55.775000+00:00" -} -``` - ---- - -## Debug - -Checking state machine definition -```sh -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] -``` - -Checking state machine execution flow, execution and state variables -```sh -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -Checking state machine states execution variables -```sh -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) - -[Top](#contents) - diff --git a/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt deleted file mode 100755 index 65ccc920..00000000 --- a/local-test-samples/stepfunctions-helloworld/aws-stepfunctions-local-credentials.txt +++ /dev/null @@ -1,3 +0,0 @@ -AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE -AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY -AWS_DEFAULT_REGION=eu-west-1 diff --git a/local-test-samples/stepfunctions-lambda/README.md b/local-test-samples/stepfunctions-lambda/README.md deleted file mode 100755 index 19202a57..00000000 --- a/local-test-samples/stepfunctions-lambda/README.md +++ /dev/null @@ -1,195 +0,0 @@ -[![nodejs: 22](https://img.shields.io/badge/NodeJS-22-green)](https://img.shields.io/badge/NodeJS-22-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# Local: AWS Step Functions with Lambda Integration - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines that integrate with Lambda functions locally. It showcases a mathematical workflow that performs sum and square operations using separate Lambda functions. - ---- - -## Contents -- [Local: AWS Step Functions with Lambda Integration](#local-aws-step-functions-with-lambda-integration) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Local Setup](#local-setup) - - [Testing Process](#testing-process) - - [State Machine Workflow](#state-machine-workflow) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- AWS Step Functions with Lambda Integration -

- -Components: -- Step Functions state machine for orchestration -- Two Node.js Lambda functions - - Sum function: adds three numbers - - Square function: squares a number -- Wait state for demonstration - ---- - -## Project Structure -``` -├── stepfunctions-lambda _# folder containing necessary code and template for Step Functions with Lambda integration_ -│ ├── img/stepfunctions-lambda.png _# Architecture diagram_ -│ ├── lambda_stepfunctions_src _# folder containing source code for Step Functions Lambda functions_ -│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for Step Functions with Lambda integration_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ -``` - ---- - -## Prerequisites -- AWS SAM CLI -- Docker -- Node.js 22 - ---- - -## Local Setup - -1. Configure environment: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -2. Start Lambda emulator: -```sh -sam local start-lambda -p 3001 --docker-network host & -``` - -3. Start Step Functions local: -```sh -docker run -d --network host \ - --name stepfunctions -p 8083:8083 \ - --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local -``` - ---- - -## Testing Process - -### 1. Test Individual Lambda Functions -```sh -# Test Sum Lambda -aws lambda invoke \ - --function-name StepFunctionExampleSumLambda \ - --endpoint-url http://127.0.0.1:3001 \ - --payload '{ "x": 6, "y": 4, "z": 9 }' \ - output.txt - -# Test Square Lambda -aws lambda invoke \ - --function-name StepFunctionExampleSquareLambda \ - --endpoint-url http://127.0.0.1:3001 \ - --payload '{ "sum": 6 }' \ - output.txt -``` - -### 2. Create State Machine -```sh -aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ - --definition "{ \ - \"StartAt\": \"Lambda Sum State\", \ - \"States\": { \ - \"Lambda Sum State\": { \ - \"Next\": \"Wait State\", \ - \"Type\": \"Task\", \ - \"InputPath\": \"$\", \ - \"OutputPath\": \"$.Payload\", \ - \"Resource\": \"arn:aws:states:::lambda:invoke\", \ - \"Parameters\": { \ - \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda\",\ - \"Payload.$\": \"$\" \ - } \ - }, \ - \"Wait State\": { \ - \"Type\": \"Wait\", \ - \"Seconds\": 3, \ - \"Next\": \"Lambda Square State\" \ - }, \ - \"Lambda Square State\": { \ - \"End\": true, \ - \"Type\": \"Task\", \ - \"InputPath\": \"$.Payload\", \ - \"OutputPath\": \"$.Payload\", \ - \"Resource\": \"arn:aws:states:::lambda:invoke\", \ - \"Parameters\": { \ - \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda\",\ - \"Payload.$\": \"$\" \ - } \ - } \ - }, \ - \"TimeoutSeconds\": 300 \ - }" --name "StepFunctionsLambdaStateMachine" --role-arn "arn:aws:iam::123456789012:role/DummyRole" -``` - -### 3. Execute State Machine -```sh -aws stepfunctions start-execution \ - --endpoint http://localhost:8083 \ - --state-machine [STATE-MACHINE-ARN] \ - --input '{"x": 2s "y": 8, "z": 7}' -``` - ---- - -## State Machine Workflow - -1. **Sum State**: Adds three input numbers -2. **Wait State**: Pauses for 3 seconds -3. **Square State**: Squares the sum result - -Example execution: -```json -Input: {"x": 2, "y": 8, "z": 7} -Sum Result: 17 -Final Result: 289 -``` - ---- - -## Debug - -Checking state machine definition -```sh -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] -``` - -Checking state machine execution flow, execution and state variables -```sh -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -Checking state machine states execution variables -```sh -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) -- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) - -[Top](#contents) - diff --git a/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt deleted file mode 100755 index 744075e4..00000000 --- a/local-test-samples/stepfunctions-lambda/aws-stepfunctions-local-credentials.txt +++ /dev/null @@ -1,4 +0,0 @@ -AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE -AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY -AWS_DEFAULT_REGION=us-east-1 -LAMBDA_ENDPOINT=http://127.0.0.1:3001 diff --git a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js deleted file mode 100755 index d0a537f5..00000000 --- a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.handler = async (event, context) => { - - const sum = event.sum; - - return { - 'Payload': { - 'result': sum * sum - } - } - -}; \ No newline at end of file diff --git a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js b/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js deleted file mode 100755 index 5d0dbeab..00000000 --- a/local-test-samples/stepfunctions-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.handler = async (event, context) => { - - const x = event.x; - const y = event.y; - const z = event.z; - - const sum = x + y + z; - - return { - 'Payload': { - 'result': sum - } - } - -}; \ No newline at end of file diff --git a/local-test-samples/stepfunctions-mock/README.md b/local-test-samples/stepfunctions-mock/README.md deleted file mode 100755 index 6128e588..00000000 --- a/local-test-samples/stepfunctions-mock/README.md +++ /dev/null @@ -1,172 +0,0 @@ -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) -[![AWS: SQS](https://img.shields.io/badge/AWS-SQS-green)](https://img.shields.io/badge/AWS-SQS-green) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# Local: AWS Step Functions with Service Mocking - -## Introduction - -This project demonstrates how to test AWS Step Functions workflows locally using service mocks. It showcases different testing scenarios including happy path, retry path, and hybrid testing approaches. - ---- - -## Contents -- [Local: AWS Step Functions with Service Mocking](#local-aws-step-functions-with-service-mocking) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Test Scenarios](#test-scenarios) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Local Setup](#local-setup) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - - -## Architecture Overview -

- AWS Step Functions with Service Mocking -

- -Components: -- Step Functions state machine -- Lambda function integration -- SQS message publishing -- Mock service responses - ---- - -## Project Structure -``` -└── stepfunctions-mock _# folder containing necessary files for mocking Step Functions_ -│ ├── img/stepfunctions-mock.png _# Architecture diagram_ -│ ├── MockConfigFile.json _# json file defining state machine implementing MOCK Step Functions_ -│ ├── aws-stepfunctions-local-credentials.txt _# file containing environment variables for mocking Step Functions_ -│ ├── README.md _# instructions file_ -│ └── statemachine.json _# json configuration file containing step machines definition for Step Functions MOCK testing_ -``` - ---- - -## Test Scenarios - -### 1. Happy Path -- Mocks both Lambda and SQS services -- All operations succeed -- Tests basic workflow flow - -### 2. Retry Path -- Tests Lambda function retry logic -- Simulates Lambda failures followed by success -- Verifies retry policy effectiveness - -### 3. Hybrid Path -- Mocks Lambda service -- Uses actual SQS service -- Tests integration with real AWS services - ---- - -## Project Structure -``` -stepfunctions-mock/ -├── MockConfigFile.json # Mock service configurations -├── aws-stepfunctions-local-credentials.txt -└── README.md -``` - ---- - -## Prerequisites -- Docker -- AWS CLI v2 -- Basic understanding of Step Functions -- Mock configuration file - ---- - -## Local Setup - -1. Start Step Functions local with mocks: -```sh -docker run -d -p 8083:8083 \ - --mount type=bind,readonly,source=$(pwd)/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ - --env-file aws-stepfunctions-local-credentials.txt \ - amazon/aws-stepfunctions-local -``` - -2. Configure environment: -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - ---- - -## Testing Workflows - -### Happy Path Testing -```sh -aws stepfunctions start-execution \ - --endpoint http://localhost:8083 \ - --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HappyPath" -``` - -### Retry Path Testing -```sh -aws stepfunctions start-execution \ - --endpoint http://localhost:8083 \ - --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#RetryPath" -``` - -### Hybrid Path Testing -```sh -aws stepfunctions start-execution \ - --endpoint http://localhost:8083 \ - --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HybridPath" -``` - -Check execution results: -```sh -aws stepfunctions describe-execution \ - --endpoint http://localhost:8083 \ - --execution-arn "" -``` - ---- - -## Debug - -Checking state machine definition -```sh -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] -``` - -Checking state machine execution flow, execution and state variables -```sh -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -Checking state machine states execution variables -```sh -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - ---- - -## Additional Resources -- [Step Functions Local Testing Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-test-sm-exec.html) -- [Service Mocking Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-mock-cfg-file.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) - -[Top](#contents) - diff --git a/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt b/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt deleted file mode 100755 index 65ccc920..00000000 --- a/local-test-samples/stepfunctions-mock/aws-stepfunctions-local-credentials.txt +++ /dev/null @@ -1,3 +0,0 @@ -AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE -AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY -AWS_DEFAULT_REGION=eu-west-1 diff --git a/python-test-samples/step-functions-local-helloworld/README.md b/python-test-samples/step-functions-local-helloworld/README.md new file mode 100644 index 00000000..d662bbe6 --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/README.md @@ -0,0 +1,194 @@ +[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happty path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local$ cd tests +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local$ cd tests +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents) \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/img/stepfunctions-helloworld-states.png b/python-test-samples/step-functions-local-helloworld/img/stepfunctions-helloworld-states.png new file mode 100644 index 0000000000000000000000000000000000000000..b70ec967c1ac7feb0401e6d0ea0b249740ef1edb GIT binary patch literal 9614 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 literal 0 HcmV?d00001 diff --git a/local-test-samples/stepfunctions-helloworld/img/stepfunctions-helloworld.png b/python-test-samples/step-functions-local-helloworld/img/stepfunctions-helloworld.png old mode 100755 new mode 100644 similarity index 100% rename from local-test-samples/stepfunctions-helloworld/img/stepfunctions-helloworld.png rename to python-test-samples/step-functions-local-helloworld/img/stepfunctions-helloworld.png diff --git a/python-test-samples/step-functions-local-helloworld/statemachine/local_testing.asl.json b/python-test-samples/step-functions-local-helloworld/statemachine/local_testing.asl.json new file mode 100644 index 00000000..4b0867f7 --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/statemachine/local_testing.asl.json @@ -0,0 +1,10 @@ +{ + "Comment": "Hello World State Machine of the Amazon States Language using a Pass state", + "StartAt": "HelloWorld", + "States": { + "HelloWorld": { + "Type": "Pass", + "End": true + } + } +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/statemachine/test/valid_input.json b/python-test-samples/step-functions-local-helloworld/statemachine/test/valid_input.json new file mode 100644 index 00000000..6a6ec7a7 --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/statemachine/test/valid_input.json @@ -0,0 +1,6 @@ +{ + "data": { + }, + "comments": "" + } +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/tests/requirements.txt b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt new file mode 100644 index 00000000..30cd543d --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt @@ -0,0 +1,4 @@ +boto3>=1.26.79 +pytest>=7.2.1 +pathlib +testcontainers \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/tests/unit/src/test_step_functions_local.py b/python-test-samples/step-functions-local-helloworld/tests/unit/src/test_step_functions_local.py new file mode 100644 index 00000000..ae4a7293 --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/tests/unit/src/test_step_functions_local.py @@ -0,0 +1,167 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +"""Tests Step Functions local with mocks using pytest""" +import os +import time +import logging +from pathlib import Path +import pytest +import boto3 +from botocore.exceptions import ClientError +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + +log = logging.getLogger() + +@pytest.fixture(name="container", scope="session") +def fixture_container(request): + """ + Runs the amazon/aws-stepfunctions-local container without the MockConfigFile + """ + sf_local = DockerContainer("amazon/aws-stepfunctions-local") \ + .with_bind_ports(8083, 8083) \ + .with_exposed_ports(8083) + + # Start the container + sf_local.start() + wait_for_logs(sf_local, "Starting server on port 8083") + + # Important to avoid non-deterministic behavior, waiting for container to spin up + time.sleep(2) + + def stop_step_function(): + log.info("[fixture] stopping step functions container") + sf_local.stop() + + request.addfinalizer(stop_step_function) + + return sf_local + +@pytest.fixture(name="sfn_client", scope="session", autouse=True) +def fixture_sfn_client(container): + """ + Creates the state machine using the local_testing.asl.json definition + """ + + # Set up Step Function client with test container URL + client = boto3.client('stepfunctions', + endpoint_url='http://' + container.get_container_host_ip() + ':' + + container.get_exposed_port(8083)) + + # Read state machine definition + step_function_definition = Path( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'statemachine', + 'local_testing.asl.json')).read_text(encoding="utf-8") + + # Create state machine + try: + client.create_state_machine( + name="HelloWorldStateMachine", + definition=step_function_definition, + roleArn="arn:aws:iam::123456789012:role/DummyRole" + ) + except ClientError as err: + log.error( + "Couldn't create state machine. Here's why: %s: %s", + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + return client + + +def get_arn(sfn_client): + """ + Get state machine ARN + """ + state_machine_arn = sfn_client.list_state_machines()["stateMachines"][0]["stateMachineArn"] + + return state_machine_arn + +def execute_stepfunction(sfn_client, execution_name, test_name=None): + """ + Executes the step function with an empty input + Returns a history of the state transitions once the step function is complete + + @param: test_name - No longer used with simple Hello World example + """ + state_machine_arn = get_arn(sfn_client) + + # Empty input is sufficient for Hello World Pass state + step_function_input = "{}" + + try: + # Starting execution of the state machine + start_execution = sfn_client.start_execution( + name=execution_name, + stateMachineArn=state_machine_arn, # No longer appending test_name + input=step_function_input + ) + except ClientError as err: + log.error( + "Couldn't start state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + while True: + time.sleep(1) + + # Checking whether the execution has completed. + try: + history_response = sfn_client.get_execution_history( + executionArn=start_execution['executionArn']) + except ClientError as err: + log.error( + "Couldn't fetch execution history for state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + success = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + success = True + break + + if success: + break + + return history_response + + +def check_state_exited_event_details(history_response, state_name): + """ + Test utility - checks if a state with the given name was exited during execution + Works with any state type (Pass, Task, etc.) + """ + for event in history_response['events']: + if 'stateExitedEventDetails' in event and event['stateExitedEventDetails']['name'] == state_name: + return True + + return False + +def test_happy_path(sfn_client): + """ + Testing that the hello world step function completes successfully. + Verifies that the state machine exits from the HelloWorld state. + """ + history_response = execute_stepfunction(sfn_client, 'happyPathExecution') + + # Check if execution succeeded + execution_succeeded = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + execution_succeeded = True + break + + assert execution_succeeded, "Step function execution did not succeed" + + # Check if HelloWorld state was exited + assert check_state_exited_event_details(history_response, 'HelloWorld'), "HelloWorld state was not exited" \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/README.md b/python-test-samples/step-functions-local-lambda/README.md new file mode 100644 index 00000000..f3aa886f --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/README.md @@ -0,0 +1,362 @@ +[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# Local: AWS Step Functions with Lambda Integration + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines that integrate with Lambda functions locally. It showcases a mathematical workflow that performs sum and square operations using separate Lambda functions, all tested through PyTest. + +--- + +## Contents +- [Local: AWS Step Functions with Lambda Integration](#local-aws-step-functions-with-lambda-integration) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Common Issues](#common-issues) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ AWS Step Functions with Lambda Integration +

+ +Components: +- Step Functions state machine for orchestration +- Two Python Lambda functions + - Sum function: adds three numbers adds three numbers (x + y + z) + - Square function: squares an entry number +- Wait state for demonstration + +

+ AWS Step Functions with Lambda Integration States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-lambda-states.png _# step functions Lambda state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── lambda_stepfunctions_src _# folder containing source code for Step Functions Lambda functions_ +├── statemachine/ +│ └── test/ _# folder containing valid inputs json input files for the different tests_ +│ └── local_testing.asl.json _# json file containing Lambda state machine definition_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirements dependencies file_ +│ ├── README.md _# instructions file_ +│ └── template.yaml _# sam yaml template file for necessary components test_ +``` + +--- + +## Prerequisites +- Docker +- Python 3.10 or newer (running pytest) +- AWS SAM CLI (running SAM Lambda emulator) +- AWS CLI v2 (for debugging) +- Basic understanding of Step Functions +- Basic understanding of Lambda Functions + +--- + +## Test Scenarios + +### 1. Lambda Sum Operation +- Tests the Lambda function that adds three numbers (x, y, z) +- Verifies the correct calculation and response structure + +### 2. Lambda Square Operation +- Tests the Lambda function that squares an input number +- Verifies the correct calculation and response structure + +### 3. Step Functions Sum-Square Workflow +- Tests the full workflow execution: + 1. Sum three numbers (x + y + z) + 2. Wait for 3 seconds + 3. Square the result (sum^2) +- Verifies that all states are exited successfully +- Checks final result matches expected calculation + +### 4. Large Number Handling +- Tests the workflow with larger numbers to ensure no integer overflow issues +- Uses large values for x, y, z (9999, 8888, 7777) +- Validates correct calculation of sum and square operations + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the services and state machine: + +1. **SAM Lambda Verification**: The `lambda_container` fixture verifies that the SAM Local Lambda emulator is running on port 3001. + +2. **Step Functions Container**: The `sfn_container` fixture starts the `amazon/aws-stepfunctions-local` Docker container with host network mode to allow communication with the Lambda emulator. + +3. **State Machine Creation**: The `sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +4. **Test Execution**: Each test either: + - Directly tests a Lambda function using the Lambda client + - Executes the state machine with `execute_stepfunction` and validates the results + +5. **Cleanup**: After tests complete, the container is automatically shut down by the finalizer in the `sfn_container` fixture. +6. + +--- + +--- + +## Testing Workflows + +### Setup Environment + +> Make sure Docker engine is running before running the tests. + +```shell +$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set up the Python environment: + +```shell +$ cd tests +$ python3 -m venv venv +$ source venv/bin/activate +$ pip install --upgrade pip +$ pip install -r requirements.txt +``` + +### Start SAM Local Lambda Emulator + +Start the SAM Local Lambda emulator in a separate terminal: + +```shell +$ sam local start-lambda -p 3001 --docker-network host +``` + +### Run the Unit Tests + +```shell +$ cd tests +$ python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + +Expected output: + +``` +$ python3 -m pytest -s unit/src/test_step_functions_local.py -v +================================================================= test session starts ================================================================= +platform linux -- Python 3.10.12, pytest-8.3.5, pluggy-1.5.0 -- /home/ubuntu/environment/step-functions-local-lambda/tests/venv/bin/python3 +cachedir: .pytest_cache +rootdir: /home/ubuntu/environment//step-functions-local-lambda/tests +plugins: xdist-3.5.0, timeout-2.3.1 +collected 4 items + +unit/src/test_step_functions_local.py::test_lambda_sum_operation Lambda sum response: {'Payload': {'result': 60}} +PASSED +unit/src/test_step_functions_local.py::test_lambda_square_operation Lambda square response: {'Payload': {'result': 81}} +PASSED +unit/src/test_step_functions_local.py::test_stepfunctions_sum_square_workflow_execution Output received: {'Payload': {'result': 289}} +PASSED +unit/src/test_step_functions_local.py::test_stepfunctions_large_number_handling Output received for large numbers: {'Payload': {'result': 710968896}} +PASSEDstepfunctions-local-test + + +================================================================= 4 passed in 16.35s ================================================================== + +``` + + +--- + +## Debug + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Start Lambda emulator + +```sh +sam local start-lambda -p 3001 --docker-network host & +``` + +#### Test Individually Lambda Functions +```sh +# Test Sum Lambda +aws lambda invoke \ + --function-name StepFunctionExampleSumLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload '{"x": 10, "y": 20, "z": 30}' \ + output.txt + +# Test Square Lambda +aws lambda invoke \ + --function-name StepFunctionExampleSquareLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload '{"result": 9}' \ + output.txt +``` + +#### Start Step Functions local: + +```sh +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local +``` + +### 2. Create State Machine +```sh +aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ + --definition "{ \ + \"StartAt\": \"Lambda Sum State\", \ + \"States\": { \ + \"Lambda Sum State\": { \ + \"Next\": \"Wait State\", \ + \"Type\": \"Task\", \ + \"InputPath\": \"$\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda\",\ + \"Payload.$\": \"$\" \ + } \ + }, \ + \"Wait State\": { \ + \"Type\": \"Wait\", \ + \"Seconds\": 3, \ + \"Next\": \"Lambda Square State\" \ + }, \ + \"Lambda Square State\": { \ + \"End\": true, \ + \"Type\": \"Task\", \ + \"InputPath\": \"$.Payload\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda\",\ + \"Payload.$\": \"$\" \ + } \ + } \ + }, \ + \"TimeoutSeconds\": 300 \ + }" --name "StepFunctionsLambdaStateMachine" --role-arn "arn:aws:iam::123456789012:role/DummyRole" +``` + +### 3. Execute State Machine +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine [STATE-MACHINE-ARN] \ + --input '{"x": 2s "y": 8, "z": 7}' +``` + +#### Check State Machine Execution + +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` + +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python3 -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG + +# Run a specific test +python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_lambda_sum_operation +``` + +--- + +## Common Issues + +### SAM Local Connection Issues + +If tests are skipped with "Lambda invocation failed, SAM might not be running properly": +- Ensure SAM Local is running on port 3001 +- Check that you've started SAM with `--docker-network host` +- Verify the Lambda functions are correctly defined in template.yaml +- Check SAM logs for any errors + +### Step Functions Container Fails to Start + +If the Step Functions container fails to start: +- Check if the container name is already in use with `docker ps -a` +- Verify Docker permissions and network settings +- Ensure port 8083 is available + +### Lambda Function Input/Output Issues + +If Lambda functions aren't receiving or returning expected data: +- Check that the input JSON files have the correct structure +- For the sum function, ensure it has "x", "y", and "z" fields +- For the square function, ensure it has a "result" field +- Verify the Lambda handler in app.py correctly processes the input + +### Step Functions Execution Failure + +If Step Functions executions fail: +- Check the execution history with the AWS CLI +- Verify the Lambda resource ARNs in the state machine definition +- Ensure the Step Functions container's LAMBDA_ENDPOINT points to the correct SAM Local port + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) +- [AWS Lambda Local Testing](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) +- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [PyTest Documentation](https://docs.pytest.org/) +- +[Top](#contents) + diff --git a/python-test-samples/step-functions-local-lambda/img/stepfunctions-lambda-states.png b/python-test-samples/step-functions-local-lambda/img/stepfunctions-lambda-states.png new file mode 100644 index 0000000000000000000000000000000000000000..04ebde07b3a56f805209103270b0eff1c99c080e GIT binary patch literal 24587 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 literal 0 HcmV?d00001 diff --git a/local-test-samples/stepfunctions-lambda/img/stepfunctions-lambda.png b/python-test-samples/step-functions-local-lambda/img/stepfunctions-lambda.png old mode 100755 new mode 100644 similarity index 100% rename from local-test-samples/stepfunctions-lambda/img/stepfunctions-lambda.png rename to python-test-samples/step-functions-local-lambda/img/stepfunctions-lambda.png diff --git a/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.py b/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.py new file mode 100644 index 00000000..0ed3e570 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_square_src/app.py @@ -0,0 +1,10 @@ +def handler(event, context): + + sum_value = event.get('result', 0) + square_result = sum_value * sum_value + + return { + 'Payload': { + 'result': square_result + } + } \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.py b/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.py new file mode 100644 index 00000000..cf97b663 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/lambda_stepfunctions_src/lambda_stepfunctions_sum_src/app.py @@ -0,0 +1,13 @@ +def handler(event, context): + + x = event.get('x', 0) + y = event.get('y', 0) + z = event.get('z', 0) + + sum_result = x + y + z + + return { + 'Payload': { + 'result': sum_result + } +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json b/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json new file mode 100644 index 00000000..fa3982d8 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json @@ -0,0 +1,34 @@ +{ + "Comment": "This state machine is called: MathOperationsStateMachine", + "StartAt": "Lambda Sum State", + "States": { + "Lambda Sum State": { + "Type": "Task", + "Resource": "arn:aws:states:::lambda:invoke", + "Parameters": { + "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda", + "Payload.$": "$" + }, + "InputPath": "$", + "OutputPath": "$.Payload", + "Next": "Wait State" + }, + "Wait State": { + "Type": "Wait", + "Seconds": 3, + "Next": "Lambda Square State" + }, + "Lambda Square State": { + "Type": "Task", + "Resource": "arn:aws:states:::lambda:invoke", + "Parameters": { + "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda", + "Payload.$": "$" + }, + "InputPath": "$.Payload", + "OutputPath": "$.Payload", + "End": true + } + }, + "TimeoutSeconds": 300 +} diff --git a/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_square.json b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_square.json new file mode 100644 index 00000000..2da97b4e --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_square.json @@ -0,0 +1,3 @@ +{ + "result": 9 +} diff --git a/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_sum.json b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_sum.json new file mode 100644 index 00000000..bcef5a2b --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_lambda_sum.json @@ -0,0 +1,5 @@ +{ + "x": 10, + "y": 20, + "z": 30 +} diff --git a/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_large_numbers.json b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_large_numbers.json new file mode 100644 index 00000000..51e82d1f --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_large_numbers.json @@ -0,0 +1,5 @@ +{ + "x": 9999, + "y": 8888, + "z": 7777 +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_sum_square.json b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_sum_square.json new file mode 100644 index 00000000..158f6866 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input_stepfunctions_sum_square.json @@ -0,0 +1,5 @@ +{ + "x": 2, + "y": 8, + "z": 7 +} diff --git a/local-test-samples/stepfunctions-lambda/template.yaml b/python-test-samples/step-functions-local-lambda/template.yaml similarity index 97% rename from local-test-samples/stepfunctions-lambda/template.yaml rename to python-test-samples/step-functions-local-lambda/template.yaml index 0b33ce8e..b5cf127c 100755 --- a/local-test-samples/stepfunctions-lambda/template.yaml +++ b/python-test-samples/step-functions-local-lambda/template.yaml @@ -15,7 +15,7 @@ Resources: FunctionName: StepFunctionExampleSumLambda CodeUri: lambda_stepfunctions_src/lambda_stepfunctions_sum_src Handler: app.handler - Runtime: nodejs22.x + Runtime: python3.10 Environment: Variables: AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE @@ -29,7 +29,7 @@ Resources: FunctionName: StepFunctionExampleSquareLambda CodeUri: lambda_stepfunctions_src/lambda_stepfunctions_square_src Handler: app.handler - Runtime: nodejs22.x + Runtime: python3.10 Environment: Variables: AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE diff --git a/python-test-samples/step-functions-local-lambda/tests/requirements.txt b/python-test-samples/step-functions-local-lambda/tests/requirements.txt new file mode 100644 index 00000000..b82bf3b5 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/tests/requirements.txt @@ -0,0 +1,8 @@ +pytest==8.3.5 +boto3==1.34.25 +testcontainers==3.7.1 +botocore==1.34.25 +pytest-timeout==2.3.1 +pytest-xdist==3.5.0 +python-dotenv==1.0.1 +requests>=2.31.0 \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py b/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py new file mode 100644 index 00000000..6e10fbdb --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py @@ -0,0 +1,374 @@ +"""Tests Step Functions local with Lambda integrations using pytest""" +import os +import time +import json +import logging +import subprocess +from pathlib import Path +import pytest +import boto3 +import requests +from botocore.exceptions import ClientError +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + +log = logging.getLogger() + +SAM_LAMBDA_PORT = 3001 + +@pytest.fixture(name="lambda_container", scope="session") +def fixture_lambda_container(request): + """ + Verifies SAM local Lambda emulator is running + """ + # Try to connect using requests + try: + response = requests.get(f"http://127.0.0.1:{SAM_LAMBDA_PORT}/2018-06-01/ping", timeout=2) + if response.status_code == 200: + log.info(f"SAM Local is available at port {SAM_LAMBDA_PORT}") + else: + log.warning(f"SAM Local responded with status code {response.status_code}") + except subprocess.CalledProcessError: + log.warning(f"SAM Local not detected at port {SAM_LAMBDA_PORT}. Tests may fail.") + + yield None + + +@pytest.fixture(name="sfn_container", scope="session") +def fixture_sfn_container(request, lambda_container): + """ + Runs the amazon/aws-stepfunctions-local container + + Host network is required for proper SAM Lambda integration as it allows the + Step Functions container to directly access the Lambda emulator on the host. + We use subprocess to run Docker commands directly since it provides more + reliable control over host networking than testcontainers Python Docker libraries. + + """ + # Get Docker command directly - testcontainers doesn't support host network mode + container_name = "stepfunctions-local-test" + + # Stop previous container if exists + try: + subprocess.run(["docker", "rm", "-f", container_name], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False) + except Exception: + pass + + # Start container with host network + cmd = [ + "docker", "run", "--rm", "-d", + "--name", container_name, + "--network", "host", + "-e", "AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE", + "-e", "AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY", + "-e", "AWS_DEFAULT_REGION=us-east-1", + "-e", f"LAMBDA_ENDPOINT=http://127.0.0.1:{SAM_LAMBDA_PORT}", + "amazon/aws-stepfunctions-local" + ] + + container_id = subprocess.check_output(cmd).decode().strip() + log.info(f"Started Step Functions container: {container_id}") + + # Wait for container to be ready + time.sleep(5) + + # Check if container is running + result = subprocess.run( + ["docker", "logs", container_name], + capture_output=True, + text=True + ) + if "Starting server on port 8083" not in result.stdout: + log.warning("Step Functions container might not be ready. Check container logs.") + time.sleep(5) + + # Create client + client = boto3.client('stepfunctions', + endpoint_url='http://localhost:8083', + aws_access_key_id="DUMMYIDEXAMPLE", + aws_secret_access_key="DUMMYEXAMPLEKEY", + region_name="us-east-1") + + def stop_step_function(): + log.info(f"[fixture] stopping step functions container {container_name}") + subprocess.run(["docker", "stop", container_name], check=False) + + request.addfinalizer(stop_step_function) + + return {'client': client, 'container_id': container_id, 'container_name': container_name} + + +@pytest.fixture(name="sfn_client", scope="session", autouse=True) +def fixture_sfn_client(sfn_container): + """ + Creates the state machine for Lambda integration testing + """ + # Get the client from sfn_container + client = sfn_container['client'] + + # Read state machine definition + step_function_definition = Path( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'statemachine', + 'local_testing.asl.json')).read_text(encoding="utf-8") + + # Create state machine + try: + client.create_state_machine( + name="LocalTesting", + definition=step_function_definition, + roleArn="arn:aws:iam::123456789012:role/DummyRole" + ) + except ClientError as err: + log.error( + "Couldn't create state machine. Here's why: %s: %s", + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + return client + + +def get_arn(sfn_client): + """ + Get state machine ARN + """ + state_machine_arn = sfn_client.list_state_machines()["stateMachines"][0]["stateMachineArn"] + return state_machine_arn + + +def execute_stepfunction(sfn_client, execution_name, input_data, max_wait_seconds=30): + """ + Executes the step function with the provided input + Returns a history of the state transitions once the step function is complete + """ + state_machine_arn = get_arn(sfn_client) + + try: + # Starting execution of the state machine + start_execution = sfn_client.start_execution( + name=execution_name, + stateMachineArn=state_machine_arn, + input=json.dumps(input_data) + ) + except ClientError as err: + log.error( + "Couldn't start state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + start_time = time.time() + while time.time() - start_time < max_wait_seconds: + time.sleep(1) + + # Checking whether the execution has completed. + try: + execution_response = sfn_client.describe_execution( + executionArn=start_execution['executionArn']) + + if execution_response['status'] in ['SUCCEEDED', 'FAILED', 'TIMED_OUT', 'ABORTED']: + # Get the execution history + history_response = sfn_client.get_execution_history( + executionArn=start_execution['executionArn']) + + # Log details if execution failed + if execution_response['status'] != 'SUCCEEDED': + log.error(f"Execution failed with status: {execution_response['status']}") + if 'error' in execution_response: + log.error(f"Error: {execution_response.get('error')}") + if 'cause' in execution_response: + log.error(f"Cause: {execution_response.get('cause')}") + + return history_response, execution_response + + except ClientError as err: + log.error( + "Couldn't fetch execution details for state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + raise TimeoutError(f"Execution did not complete within {max_wait_seconds} seconds") + + +def check_state_exited_event_details(history_response, state_name): + """ + Test utility - used to verify which state the task exited in + """ + success = False + + for event in history_response['events']: + # Check for regular Task state exits + if event['type'] == 'TaskStateExited' and 'stateExitedEventDetails' in event and \ + event['stateExitedEventDetails']['name'] == state_name: + success = True + break + + # Check for Wait state exits - they use a different event type + if event['type'] == 'WaitStateExited' and 'stateExitedEventDetails' in event and \ + event['stateExitedEventDetails']['name'] == state_name: + success = True + break + + # Generic check for any state exit + if 'stateExitedEventDetails' in event and \ + event['stateExitedEventDetails']['name'] == state_name: + success = True + break + + return success + + +def test_lambda_sum_operation(sfn_client): + """ + Test just the Lambda sum operation is working correctly + """ + + # Load JSON input file and parse it + input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', + 'valid_input_lambda_sum.json')).read_text(encoding="utf-8") + input_data = json.loads(input_data_str) # Parse the JSON string into a dictionary + + # Expected sum x + y + z = 10 + 20 + 30 = 60 + expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 60 + + # Create a lambda client to directly test the function + lambda_client = boto3.client('lambda', + endpoint_url=f'http://127.0.0.1:{SAM_LAMBDA_PORT}', + aws_access_key_id="DUMMYIDEXAMPLE", + aws_secret_access_key="DUMMYEXAMPLEKEY", + region_name="us-east-1") + + # Test sum lambda directly + try: + response = lambda_client.invoke( + FunctionName='StepFunctionExampleSumLambda', + Payload=json.dumps(input_data) + ) + + # Parse response + result = json.loads(response['Payload'].read()) + print(f"Lambda sum response: {result}") + + # Check result based on the actual structure + assert 'Payload' in result, "Response missing 'Payload' field" + assert 'result' in result['Payload'], "Payload missing 'result' field" + assert result['Payload']['result'] == expected_sum, f"Expected sum {expected_sum}, got {result['Payload']['result']}" + except Exception as e: + pytest.skip(f"Lambda invocation failed, SAM might not be running properly: {e}") + + +def test_lambda_square_operation(sfn_client): + """ + Test just the Lambda square operation is working correctly + """ + + # Load JSON input file and parse it + input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', + 'valid_input_lambda_square.json')).read_text(encoding="utf-8") + input_data = json.loads(input_data_str) + + # Expected 9^2 = 9 * 9 = 81 + expected_square = input_data["result"] * input_data["result"] # 81 + + # Create a lambda client to directly test the function + lambda_client = boto3.client('lambda', + endpoint_url=f'http://127.0.0.1:{SAM_LAMBDA_PORT}', + aws_access_key_id="DUMMYIDEXAMPLE", + aws_secret_access_key="DUMMYEXAMPLEKEY", + region_name="us-east-1") + + # Test square lambda directly + try: + response = lambda_client.invoke( + FunctionName='StepFunctionExampleSquareLambda', + Payload=json.dumps(input_data) + ) + + # Parse response + payload_bytes = response['Payload'].read() + result = json.loads(payload_bytes) + print(f"Lambda square response: {result}") + + # Check result based on the actual structure + assert 'Payload' in result, "Response missing 'Payload' field" + assert 'result' in result['Payload'], "Payload missing 'result' field" + assert result['Payload']['result'] == expected_square, f"Expected square {expected_square}, got {result['Payload']['result']}" + except Exception as e: + pytest.skip(f"Lambda invocation failed, SAM might not be running properly: {e}") + + +def test_stepfunctions_sum_square_workflow_execution(sfn_client): + """ + Test full execution of the mathematical workflow: + 1. Sum three numbers (x + y + z) + 2. Wait for 3 seconds + 3. Square the result (sum^2) + """ + + # Load JSON input file and parse it + input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', + 'valid_input_stepfunctions_sum_square.json')).read_text(encoding="utf-8") + input_data = json.loads(input_data_str) + + # Expected sum and final result + expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 17 + expected_final = expected_sum * expected_sum # 289 + + # Execute the state machine + history_response, execution_response = execute_stepfunction( + sfn_client, 'mathWorkflowExecution', input_data) + + # Check execution succeeded + assert execution_response['status'] == 'SUCCEEDED', "Execution did not succeed" + + # Verify output contains the expected result + output = json.loads(execution_response['output']) + print(f"Output received: {output}") + + # Check for the result in the Payload structure + assert 'Payload' in output, "Output missing 'Payload' field" + assert 'result' in output['Payload'], "Payload missing 'result' field" + assert output['Payload']['result'] == expected_final, f"Expected result {expected_final}, got {output['Payload']['result']}" + + # Check that all states exited successfully + assert check_state_exited_event_details(history_response, 'Lambda Sum State') + assert check_state_exited_event_details(history_response, 'Wait State') + assert check_state_exited_event_details(history_response, 'Lambda Square State') + + +def test_stepfunctions_large_number_handling(sfn_client): + """ + Test handling of larger numbers to ensure no integer overflow issues + """ + + # Load JSON input file and parse it + input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', + 'valid_input_stepfunctions_large_numbers.json')).read_text(encoding="utf-8") + input_data = json.loads(input_data_str) + + # Expected results + expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 26664 + expected_final = expected_sum * expected_sum # 710968896 + + # Execute the state machine + history_response, execution_response = execute_stepfunction( + sfn_client, 'largeNumberExecution', input_data) + + # Check execution succeeded + assert execution_response['status'] == 'SUCCEEDED', "Execution did not succeed" + + # Verify output contains the expected result + output = json.loads(execution_response['output']) + print(f"Output received for large numbers: {output}") + + # Check for the result in the Payload structure + assert 'Payload' in output, "Output missing 'Payload' field" + assert 'result' in output['Payload'], "Payload missing 'result' field" + assert output['Payload']['result'] == expected_final, f"Expected result {expected_final}, got {output['Payload']['result']}" \ No newline at end of file diff --git a/python-test-samples/step-functions-local-mock/README.md b/python-test-samples/step-functions-local-mock/README.md new file mode 100644 index 00000000..a7d50954 --- /dev/null +++ b/python-test-samples/step-functions-local-mock/README.md @@ -0,0 +1,251 @@ +[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) +[![AWS: SQS](https://img.shields.io/badge/AWS-SQS-green)](https://img.shields.io/badge/AWS-SQS-green) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# Local: AWS Step Functions with Service Mocking + +## Introduction + +This project demonstrates how to test AWS Step Functions workflows locally using service mocks. By leveraging the Step Functions Local Docker container and a mock configuration file, it showcases different testing scenarios including happy path, retry path, and hybrid testing approaches. + +--- + +## Contents +- [Local: AWS Step Functions with Service Mocking](#local-aws-step-functions-with-service-mocking) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ AWS Step Functions with Service Mocking +

+ +Components: +- Step Functions state machine with two states: + - LambdaState: Invokes a Lambda function with retry logic + - SQSState: Sends a message to an SQS queue +- Mock service responses for both Lambda and SQS + +

+ AWS Step Functions with Service Mocking States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-mock-states.png _# step functions MOCK state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── test/MockConfigFile.json _# json file defining MOCK answers from Step Funtions state machine_ +│ └── local_testing.asl.json _# json file containing MOCK state machine definition_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirements dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- Python 3.10 or newer +- AWS CLI v2 (for debugging) +- Basic understanding of Step Functions + +--- + +## Test Scenarios + +### 1. Happy Path +- Tests the scenario where both Lambda and SQS operations succeed on the first attempt +- Verifies that both states exit successfully and all tasks complete +- Used to validate the basic functionality of the state machine + +### 2. Retry Path +- Tests the state machine's retry mechanism +- Lambda fails initially with a ResourceNotReadyException and then with TimeoutExceptions +- After 3 failures, Lambda succeeds on the 4th attempt +- Verifies that the retry policy is applied correctly and the state machine can recover from transient errors + +### 3. Hybrid Path +- Tests both Lambda and SQS with mocked responses +- Verifies that the overall execution completes successfully +- Ensures both states are properly exited +- Demonstrates how to test a workflow end-to-end with mocked components + +--- + +## Testing Workflows + +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-mock$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +``` + +To set it up: + +``` shell +step-functions-local-mock$ cd tests +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + +To run the unit tests: + +``` shell +step-functions-local-mock$ cd tests +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + +expected output + +``` +step-functions-local-mock$ python3 -m pytest -s unit/src/test_step_functions_local.py -v +============================================================== test session starts ============================================================== +platform linux -- Python 3.10.12, pytest-8.3.5, pluggy-1.5.0 -- /home/ubuntu/environment/step-functions-local-mock/tests/venv/bin/python3 +cachedir: .pytest_cache +rootdir: /home/ubuntu/environment/step-functions-local-mock/tests +collected 3 items + +unit/src/test_step_functions_local.py::test_happy_path Pulling image testcontainers/ryuk:0.8.1 +Container started: 86676d42c5de +Waiting for container with image testcontainers/ryuk:0.8.1 to be ready ... +Pulling image amazon/aws-stepfunctions-local +Container started: 034b00a0307a +Waiting for container with image amazon/aws-stepfunctions-local to be ready ... +PASSED +``` + +--- + +## Debug + +### AWS CLI Commands for Manual Testing + +#### 1. Start the Step Functions Local container: + +```sh +docker run -d -p 8083:8083 \ + --mount type=bind,readonly,source=$(pwd)/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ + --env-file aws-stepfunctions-local-credentials.txt \ + amazon/aws-stepfunctions-local +``` + +#### 2. Set up environment variables: + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### 3. Create the state machine: + +```sh +aws stepfunctions create-state-machine \ + --endpoint http://localhost:8083 \ + --name LambdaSQSIntegration \ + --definition file://statemachine/local_testing.asl.json \ + --role-arn "arn:aws:iam::123456789012:role/DummyRole" +``` + +#### 4. Execute test scenarios: + +Happy Path: +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HappyPath" +``` + +Retry Path: +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#RetryPath" +``` + +Hybrid Path: +```sh +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HybridPath" +``` + +#### 5. Inspect execution results: + +Checking state machine execution +```sh +aws stepfunctions describe-execution \ + --endpoint http://localhost:8083 \ + --execution-arn "" +``` + +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` + +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed debugging in pytest: + +```sh +# Run with verbose output and show print statements +python3 -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG + +# Run only a specific test +python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_hybrid_path +``` + +--- + +## Additional Resources +- [Step Functions Local Testing Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-test-sm-exec.html) +- [Service Mocking Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-mock-cfg-file.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [Mocking Service Integrations with Step Functions Local](https://aws.amazon.com/blogs/compute/mocking-service-integrations-with-aws-step-functions-local/) +- [Step Functions Service Mocking Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-mock-cfg-file.html) +- [Amazon States Language Specification](https://states-language.net/spec.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) + + +[Top](#contents) + diff --git a/python-test-samples/step-functions-local-mock/img/stepfunctions-mock-states.png b/python-test-samples/step-functions-local-mock/img/stepfunctions-mock-states.png new file mode 100644 index 0000000000000000000000000000000000000000..30480181f45200703097caf39920c32bd6898ded GIT binary patch literal 19643 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.26.79 +pytest>=7.2.1 +pathlib +testcontainers \ No newline at end of file diff --git a/python-test-samples/step-functions-local-mock/tests/unit/src/test_step_functions_local.py b/python-test-samples/step-functions-local-mock/tests/unit/src/test_step_functions_local.py new file mode 100644 index 00000000..7182daf2 --- /dev/null +++ b/python-test-samples/step-functions-local-mock/tests/unit/src/test_step_functions_local.py @@ -0,0 +1,231 @@ +"""Tests Step Functions local with mocks using pytest for LambdaSQSIntegration""" +import os +import time +import logging +from pathlib import Path +import pytest +import boto3 +from botocore.exceptions import ClientError +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + +log = logging.getLogger() + + +@pytest.fixture(name="container", scope="session") +def fixture_container(request): + """ + Runs the amazon/aws-stepfunctions-local container with the MockConfigFile.json + """ + + mock_file_host_path = os.path.join(os.path.dirname(__file__), '..', '..', '..', + 'statemachine', 'test', 'MockConfigFile.json') + mock_file_container_path = "/home/stepfunctionslocal/MockConfigFile.json" + + sf_local = DockerContainer("amazon/aws-stepfunctions-local") \ + .with_bind_ports(8083, 8083) \ + .with_exposed_ports(8083) \ + .with_env("SFN_MOCK_CONFIG", mock_file_container_path) \ + .with_volume_mapping(mock_file_host_path, mock_file_container_path) + + sf_local.start() + wait_for_logs(sf_local, "Starting server on port 8083") + + # Important to avoid non-deterministic behavior, waiting for container to spin up + time.sleep(2) + + def stop_step_function(): + log.info("[fixture] stopping step functions container") + sf_local.stop() + + request.addfinalizer(stop_step_function) + + return sf_local + + +@pytest.fixture(name="sfn_client", scope="session", autouse=True) +def fixture_sfn_client(container): + """ + Creates the state machine using the local_testing.asl.json definition + """ + + # Set up Step Function client with test container URL + client = boto3.client('stepfunctions', + endpoint_url='http://' + container.get_container_host_ip() + ':' + + container.get_exposed_port(8083)) + + # Read state machine definition + step_function_definition = Path( + os.path.join(os.path.dirname(__file__), '..', '..', '..', 'statemachine', + 'local_testing.asl.json')).read_text(encoding="utf-8") + + # Create state machine + try: + client.create_state_machine( + name="LambdaSQSIntegration", + definition=step_function_definition, + roleArn="arn:aws:iam::123456789012:role/DummyRole" + ) + except ClientError as err: + log.error( + "Couldn't create state machine. Here's why: %s: %s", + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + return client + + +def get_arn(sfn_client): + """ + Get state machine ARN + """ + state_machine_arn = sfn_client.list_state_machines()["stateMachines"][0]["stateMachineArn"] + + return state_machine_arn + + +def execute_stepfunction(sfn_client, execution_name, test_name): + """ + Executes the step function with a sample input + Returns a history of the state transitions once the step function is complete + + @param: test_name - Used to look up the test case and mocks to use in MockConfigFile.json + """ + state_machine_arn = get_arn(sfn_client) + + # Simple input for the state machine + step_function_input = '{"data": "test input"}' + + try: + # Starting execution of the state machine + start_execution = sfn_client.start_execution( + name=execution_name, + stateMachineArn=state_machine_arn + '#' + test_name, + input=step_function_input + ) + except ClientError as err: + log.error( + "Couldn't start state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + while True: + time.sleep(1) + + # Checking whether the execution has completed. + try: + history_response = sfn_client.get_execution_history( + executionArn=start_execution['executionArn']) + except ClientError as err: + log.error( + "Couldn't fetch execution history for state machine %s. Here's why: %s: %s", + state_machine_arn, + err.response["Error"]["Code"], + err.response["Error"]["Message"], + ) + raise + + success = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + success = True + break + + if success: + break + + return history_response + + +def check_state_exited_event_details(history_response, state_exited_event_details): + """ + Test utility - used to verify which state the task exited in + """ + success = False + + for event in history_response['events']: + if event['type'] == 'TaskStateExited' and 'stateExitedEventDetails' in event and \ + event['stateExitedEventDetails']['name'] == state_exited_event_details: + success = True + break + + return success + + +def test_happy_path(sfn_client): + """ + Testing the happy path where both Lambda and SQS operations succeed on the first try. + """ + history_response = execute_stepfunction(sfn_client, 'happyPathExecution', 'HappyPath') + + # Check that both states exited successfully + assert check_state_exited_event_details(history_response, 'LambdaState') + assert check_state_exited_event_details(history_response, 'SQSState') + + # Count task states that were successful + success_count = 0 + for event in history_response['events']: + if event['type'] == 'TaskSucceeded': + success_count += 1 + + # We expect both Lambda and SQS tasks to succeed + assert success_count == 2 + + +def test_retry_path(sfn_client): + """ + Testing retry path where Lambda fails multiple times before succeeding. + The Lambda invocation is retried until it succeeds on the 4th attempt. + """ + history_response = execute_stepfunction(sfn_client, 'retryPathExecution', 'RetryPath') + + # Check that both states exited successfully + assert check_state_exited_event_details(history_response, 'LambdaState') + assert check_state_exited_event_details(history_response, 'SQSState') + + # Count Lambda failures + lambda_failures = 0 + resource_not_ready_errors = 0 + timeout_errors = 0 + + for event in history_response['events']: + if event['type'] == 'TaskFailed': + lambda_failures += 1 + if 'taskFailedEventDetails' in event: + error = event['taskFailedEventDetails']['error'] + if error == 'Lambda.ResourceNotReadyException': + resource_not_ready_errors += 1 + elif error == 'Lambda.TimeoutException': + timeout_errors += 1 + + # There should be three failures (1 ResourceNotReady, 2 Timeout) before success + assert lambda_failures == 3 + assert resource_not_ready_errors == 1 + assert timeout_errors == 2 + + +def test_hybrid_path(sfn_client): + """ + Testing hybrid path where only Lambda state is mocked and SQS uses + the mock that was specified in the default configuration. + """ + history_response = execute_stepfunction(sfn_client, 'hybridPathExecution', 'HybridPath') + + # Verify that the execution completed successfully + execution_succeeded = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + execution_succeeded = True + break + + assert execution_succeeded, "Execution did not complete successfully" + + # If the execution completed successfully, and we've verified the state transitions, + # we can assume the Lambda was successful + assert check_state_exited_event_details(history_response, 'LambdaState') + assert check_state_exited_event_details(history_response, 'SQSState') \ No newline at end of file From d1ea8b4c91b85588a0084c3eff0177cfc99ec2b9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 16 Apr 2025 10:59:54 +0000 Subject: [PATCH 3/7] Examples migrated to pytest testing - README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36e0af2a..11fd63b5 100755 --- a/README.md +++ b/README.md @@ -92,3 +92,4 @@ Emulation is not a replacement for testing against actual cloud resources, and i # How do I contribute? See our [Contributing](./CONTRIBUTING.md) guide for more detail providing additions, enhancements, and edits. + From 1b0163ca70212c151cc8995127df0d25d6491b7f Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 16 Apr 2025 11:01:08 +0000 Subject: [PATCH 4/7] Examples migrated to pytest testing - README 2 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 11fd63b5..98ef9c4a 100755 --- a/README.md +++ b/README.md @@ -67,6 +67,9 @@ Event-driven architectures (EDA) are an architecture style that uses events and |System Under Test|Language|Description| |---|---|---| | [Step Functions](./java-test-samples/step-functions-local) [External] |Java|This project shows a technique for testing an AWS Step Functions workflow in a local desktop environment.| +| [Step Functions](./java-test-samples/step-functions-local-helloworld) |Python|This project demostrates how to test a "Hello World" AWS Step Functions workflow locally using Docker and PyTest.| +| [Step Functions](./java-test-samples/step-functions-local-lambda) |Python|This project shows a technique for testing an AWS Step Functions state machines that integrate with Lambda functions locally.| +| [Step Functions](./java-test-samples/step-functions-local-mock) |Python|This project shows a technique for testing an AWS Step Functions workflows locally using service mocks.| ## Data Processing | System Under Test|Language|Description| From 40585f506f3312acdabc4868dfbd5a6917ef6107 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 15 May 2025 15:32:03 +0000 Subject: [PATCH 5/7] Updating READMEs as per PR comments --- .../step-functions-local-helloworld/README.md | 1608 ++++++++++++++++- .../tests/requirements.txt | 2 +- .../step-functions-local-lambda/README.md | 27 +- .../aws-stepfunctions-local-credentials.txt | 4 + .../aws-stepfunctions-local-credentials.txt | 4 + .../tests/requirements.txt | 4 +- 6 files changed, 1629 insertions(+), 20 deletions(-) create mode 100644 python-test-samples/step-functions-local-lambda/aws-stepfunctions-local-credentials.txt create mode 100644 python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt diff --git a/python-test-samples/step-functions-local-helloworld/README.md b/python-test-samples/step-functions-local-helloworld/README.md index d662bbe6..a0a60338 100644 --- a/python-test-samples/step-functions-local-helloworld/README.md +++ b/python-test-samples/step-functions-local-helloworld/README.md @@ -74,7 +74,7 @@ Components: ## Test Scenarios ### 1. Hello World -- Tests the happty path where a single state in step funtions succeed on the first attempt +- Tests the happy path where a single state in step funtions succeed on the first attempt - Used to validate the basic functionality of the state machine --- @@ -103,7 +103,7 @@ The test process leverages PyTest fixtures to manage the lifecycle of the Step F > Make sure docker engine is running before running the tests. ``` shell -step-functions-local$ docker version +step-functions-local-helloworld$ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 @@ -113,7 +113,1602 @@ Client: Docker Engine - Community To set it up: ``` shell -step-functions-local$ cd tests +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +``` + + +To run the unit tests: + +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` + + +Expected output: +``` +============================= test session starts ============================== +... +unit/src/test_step_functions_local.py::test_happy_path PASSED +============================== 1 passed in 28.27s ============================== +``` + +--- + +## Debug + + +### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment + +```sh +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +``` + +#### Check Execution Status + +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] + +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` + +### PyTest Debugging + +For more detailed PyTest output: + +```sh +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +``` + +--- + +## Additional Resources +- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) +- [PyTest Documentation](https://docs.pytest.org/) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) + +[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) +[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) +[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) +[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) + +# AWS Step Functions: Hello World with PyTest + +## Introduction + +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + + +--- + +## Contents +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) + - [Introduction](#introduction) + - [Contents](#contents) + - [Architecture Overview](#architecture-overview) + - [Project Structure](#project-structure) + - [Prerequisites](#prerequisites) + - [Test Scenarios](#test-scenarios) + - [About the Test Process](#about-the-test-process) + - [Testing Workflows](#testing-workflows) + - [Debug](#debug) + - [Additional Resources](#additional-resources) + +--- + +## Architecture Overview +

+ Hello World Step Functions +

+ +Components: +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution + +

+ Hello World Step Functions - States +

+ +--- + +## Project Structure +``` +├── img/ +│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ +├── statemachine/ +│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +├── tests/ +│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ +``` + +--- + +## Prerequisites +- Docker +- AWS cli (debugging) +- Python 3.10 or newer +- Basic understanding of Step Functions +- Basic understanding of Amazon States Language (ASL) + + +--- + +## Test Scenarios + +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine + +--- + +## About the Test Process + +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: + +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. + +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. + +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. + +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited + +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. + +--- + +## Testing Workflows + +### Run the Unit Test +> Make sure docker engine is running before running the tests. + +``` shell +step-functions-local-helloworld$ docker version +Client: Docker Engine - Community + Version: 24.0.6 + API version: 1.43 +... +``` + +To set it up: + +``` shell +step-functions-local-helloworld$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' python3 -m venv venv source venv/bin/activate pip install --upgrade pip @@ -124,7 +1719,7 @@ pip install -r requirements.txt To run the unit tests: ``` shell -step-functions-local$ cd tests +step-functions-local-helloworld/tests$ python3 -m pytest -s unit/src/test_step_functions_local.py -v ``` @@ -157,6 +1752,9 @@ export REGION='us-east-1' #### Check Execution Status ```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 + # Check state machine definition aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ --state-machine-arn [STATE-MACHINE-ARN] @@ -191,4 +1789,4 @@ python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEB - [PyTest Documentation](https://docs.pytest.org/) - [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) -[Top](#contents) \ No newline at end of file +[Top](#contents) diff --git a/python-test-samples/step-functions-local-helloworld/tests/requirements.txt b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt index 30cd543d..831081ef 100644 --- a/python-test-samples/step-functions-local-helloworld/tests/requirements.txt +++ b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.26.79 pytest>=7.2.1 pathlib -testcontainers \ No newline at end of file +testcontainers diff --git a/python-test-samples/step-functions-local-lambda/README.md b/python-test-samples/step-functions-local-lambda/README.md index f3aa886f..acd2fd4b 100644 --- a/python-test-samples/step-functions-local-lambda/README.md +++ b/python-test-samples/step-functions-local-lambda/README.md @@ -115,9 +115,6 @@ The test process leverages PyTest fixtures to manage the lifecycle of the servic - Executes the state machine with `execute_stepfunction` and validates the results 5. **Cleanup**: After tests complete, the container is automatically shut down by the finalizer in the `sfn_container` fixture. -6. - ---- --- @@ -128,7 +125,7 @@ The test process leverages PyTest fixtures to manage the lifecycle of the servic > Make sure Docker engine is running before running the tests. ```shell -$ docker version +step-functions-local-lambda$ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 @@ -138,7 +135,7 @@ Client: Docker Engine - Community To set up the Python environment: ```shell -$ cd tests +step-functions-local-lambda$ cd tests $ python3 -m venv venv $ source venv/bin/activate $ pip install --upgrade pip @@ -150,14 +147,18 @@ $ pip install -r requirements.txt Start the SAM Local Lambda emulator in a separate terminal: ```shell -$ sam local start-lambda -p 3001 --docker-network host +step-functions-local-lambda/tests$ cd .. +$ sam local start-lambda -p 3001 --docker-network host & ``` ### Run the Unit Tests ```shell -$ cd tests -$ python3 -m pytest -s unit/src/test_step_functions_local.py -v +step-functions-local-lambda$ cd tests +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export REGION='us-east-1' +python3 -m pytest -s unit/src/test_step_functions_local.py -v ``` Expected output: @@ -197,6 +198,7 @@ If you need to manually verify the state machine or execution details, you can u #### Configure environment ```sh +step-functions-local-lambda$ export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' export REGION='us-east-1' @@ -205,6 +207,7 @@ export REGION='us-east-1' #### Start Lambda emulator ```sh +step-functions-local-lambda$ sam local start-lambda -p 3001 --docker-network host & ``` @@ -214,14 +217,14 @@ sam local start-lambda -p 3001 --docker-network host & aws lambda invoke \ --function-name StepFunctionExampleSumLambda \ --endpoint-url http://127.0.0.1:3001 \ - --payload '{"x": 10, "y": 20, "z": 30}' \ + --payload fileb://statemachine/test/valid_input_lambda_sum.json \ output.txt # Test Square Lambda aws lambda invoke \ --function-name StepFunctionExampleSquareLambda \ --endpoint-url http://127.0.0.1:3001 \ - --payload '{"result": 9}' \ + --payload fileb://statemachine/test/valid_input_lambda_square.json \ output.txt ``` @@ -276,7 +279,7 @@ aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ aws stepfunctions start-execution \ --endpoint http://localhost:8083 \ --state-machine [STATE-MACHINE-ARN] \ - --input '{"x": 2s "y": 8, "z": 7}' + --input '{"x": 2, "y": 8, "z": 7}' ``` #### Check State Machine Execution @@ -329,7 +332,7 @@ If tests are skipped with "Lambda invocation failed, SAM might not be running pr ### Step Functions Container Fails to Start If the Step Functions container fails to start: -- Check if the container name is already in use with `docker ps -a` +- Check if the container image "amazon/aws-stepfunctions-local" is already in use with `docker ps -a` - Verify Docker permissions and network settings - Ensure port 8083 is available diff --git a/python-test-samples/step-functions-local-lambda/aws-stepfunctions-local-credentials.txt b/python-test-samples/step-functions-local-lambda/aws-stepfunctions-local-credentials.txt new file mode 100644 index 00000000..744075e4 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,4 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 +LAMBDA_ENDPOINT=http://127.0.0.1:3001 diff --git a/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt b/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt new file mode 100644 index 00000000..744075e4 --- /dev/null +++ b/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,4 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 +LAMBDA_ENDPOINT=http://127.0.0.1:3001 diff --git a/python-test-samples/step-functions-local-mock/tests/requirements.txt b/python-test-samples/step-functions-local-mock/tests/requirements.txt index 30cd543d..cdb0c683 100644 --- a/python-test-samples/step-functions-local-mock/tests/requirements.txt +++ b/python-test-samples/step-functions-local-mock/tests/requirements.txt @@ -1,4 +1,4 @@ -boto3>=1.26.79 +boto3>=1.38.4 pytest>=7.2.1 pathlib -testcontainers \ No newline at end of file +testcontainers From da057e282eadd5c0012a510127cd2b1fb038f2d2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 1 Jul 2025 08:52:44 +0000 Subject: [PATCH 6/7] Splitting aws cli and pytest cases and adding Debug commands --- .../step-functions-local-helloworld/README.md | 1679 +---------------- .../aws-stepfunctions-local-credentials.txt | 3 + .../tests/requirements.txt | 2 +- .../step-functions-local-lambda/README.md | 364 ++-- .../statemachine/local_testing.asl.json | 38 +- .../statemachine/test/valid_input.json | 6 + .../aws-stepfunctions-local-credentials.txt | 3 + .../tests/requirements.txt | 12 +- .../unit/src/test_step_functions_local.py | 343 +--- .../step-functions-local-mock/README.md | 125 +- .../aws-stepfunctions-local-credentials.txt | 1 + .../tests/requirements.txt | 2 +- 12 files changed, 381 insertions(+), 2197 deletions(-) create mode 100644 python-test-samples/step-functions-local-helloworld/tests/aws-stepfunctions-local-credentials.txt create mode 100644 python-test-samples/step-functions-local-lambda/statemachine/test/valid_input.json create mode 100644 python-test-samples/step-functions-local-lambda/tests/aws-stepfunctions-local-credentials.txt diff --git a/python-test-samples/step-functions-local-helloworld/README.md b/python-test-samples/step-functions-local-helloworld/README.md index a0a60338..619b031d 100644 --- a/python-test-samples/step-functions-local-helloworld/README.md +++ b/python-test-samples/step-functions-local-helloworld/README.md @@ -23,7 +23,6 @@ This project demonstrates how to test AWS Step Functions state machines locally - [Test Scenarios](#test-scenarios) - [About the Test Process](#about-the-test-process) - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - [Additional Resources](#additional-resources) --- @@ -48,13 +47,14 @@ Components: ## Project Structure ``` ├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ ├── stepfunctions-helloworld-states.png _# Step Functions state flow_ │ └── stepfunctions-mock.png _# visual architecture diagram_ ├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ ├── local_testing.asl.json _# json file containing "Hello World" state machine definition_ │ └── test/valid_input.json _# json file containing "Hello World" state machine input_ ├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ ├── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ ├── aws-stepfunctions-local-credentials.txt _# Step Functions Docker image environment vars_ │ └── requirements.txt _# pip requirments dependencies file_ └── README.md _# instructions file_ ``` @@ -68,1403 +68,9 @@ Components: - Basic understanding of Step Functions - Basic understanding of Amazon States Language (ASL) - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows - -### Run the Unit Test -> Make sure docker engine is running before running the tests. - -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` - -To set it up: - -``` shell -step-functions-local-helloworld$ cd tests -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt -``` - - -To run the unit tests: - -``` shell -step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment - -```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -``` - -#### Check Execution Status - -```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] - -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] - -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` - -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - ---- - -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - --- -## Test Scenarios +## Test Scenarios (end to end python test) ### 1. Hello World - Tests the happy path where a single state in step funtions succeed on the first attempt @@ -1492,86 +98,88 @@ The test process leverages PyTest fixtures to manage the lifecycle of the Step F ## Testing Workflows -### Run the Unit Test -> Make sure docker engine is running before running the tests. +### Setup Docker Environment -``` shell -step-functions-local-helloworld$ docker version +> Make sure Docker engine is running before running the tests. + +```shell +step-functions-local-lambda$ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 -... +(...) ``` -To set it up: +### Run the Unit Test - End to end python test + +> Set up the python environment ``` shell step-functions-local-helloworld$ cd tests export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' +export AWS_REGION='us-east-1' python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt ``` - -To run the unit tests: +#### Run the unit tests ``` shell step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v +python3 -m pytest -s unit/src/test_step_functions_local.py ``` - Expected output: -``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug +``` shell +step-functions-local-helloworld/tests$ python3 -m pytest -s unit/src/test_step_functions_local.py +========================================================== test session starts ========================================================== +platform linux -- Python 3.10.12, pytest-8.4.1, pluggy-1.6.0 +rootdir: /home/ubuntu/environment/serverless-test-samples-approach-2-pytest-distributed_Try3/step-functions-local-helloworld--OK/tests +collected 1 item +unit/src/test_step_functions_local.py Pulling image testcontainers/ryuk:0.8.1 +Container started: ecdd9649b97d +Waiting for container with image testcontainers/ryuk:0.8.1 to be ready ... +Pulling image amazon/aws-stepfunctions-local +Container started: d1bd33969e6b +Waiting for container with image amazon/aws-stepfunctions-local to be ready ... +. -### AWS CLI Commands for Manual Verification +========================================================== 1 passed in 10.57s ========================================================== +( +``` -If you need to manually verify the state machine or execution details, you can use these commands: +#### Clean up section -#### Configure environment +> clean pyenv environment ```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' +step-functions-local-helloworld/tests$ +deactivate +rm -rf venv/ ``` -#### Check Execution Status +> unsetting variables ```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 - -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] +unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +unset AWS_REGION='us-east-1' +``` -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] +> cleanning docker -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] +```sh +docker ps --filter "ancestor=amazon/aws-stepfunctions-local" -q | xargs -r docker kill +docker rmi amazon/aws-stepfunctions-local ``` -### PyTest Debugging +#### Debug - PyTest Debugging -For more detailed PyTest output: +For more detailed debugging in pytest: ```sh # Run with verbose output @@ -1583,173 +191,40 @@ python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEB --- -## Additional Resources -- [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) -- [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) - -[Top](#contents)[![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) -[![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) -[![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) -[![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) - -# AWS Step Functions: Hello World with PyTest - -## Introduction - -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - - ---- - -## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - - [Introduction](#introduction) - - [Contents](#contents) - - [Architecture Overview](#architecture-overview) - - [Project Structure](#project-structure) - - [Prerequisites](#prerequisites) - - [Test Scenarios](#test-scenarios) - - [About the Test Process](#about-the-test-process) - - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Additional Resources](#additional-resources) - ---- - -## Architecture Overview -

- Hello World Step Functions -

- -Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution - -

- Hello World Step Functions - States -

- ---- - -## Project Structure -``` -├── img/ -│ └── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── statemachine/ -│ └── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ -├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ -``` - ---- - -## Prerequisites -- Docker -- AWS cli (debugging) -- Python 3.10 or newer -- Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) - - ---- - -## Test Scenarios - -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine - ---- - -## About the Test Process - -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: - -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. - -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. - -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. - -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited - -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. - ---- - -## Testing Workflows +### Fast local development for Step Functions -### Run the Unit Test -> Make sure docker engine is running before running the tests. +#### AWS CLI Commands for Manual Verification -``` shell -step-functions-local-helloworld$ docker version -Client: Docker Engine - Community - Version: 24.0.6 - API version: 1.43 -... -``` +If you need to manually verify the state machine or execution details, you can use these commands: -To set it up: +#### Configure environment variables: -``` shell -step-functions-local-helloworld$ cd tests +```sh +step-functions-local-helloworld/tests$ export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m venv venv -source venv/bin/activate -pip install --upgrade pip -pip install -r requirements.txt +export AWS_REGION='us-east-1' ``` +#### Set up state machine machine manually -To run the unit tests: - -``` shell +```sh +# Runing step functions docker image in the background step-functions-local-helloworld/tests$ -python3 -m pytest -s unit/src/test_step_functions_local.py -v -``` - - -Expected output: +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local ``` -============================= test session starts ============================== -... -unit/src/test_step_functions_local.py::test_happy_path PASSED -============================== 1 passed in 28.27s ============================== -``` - ---- - -## Debug - - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment ```sh -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' +# Creating Hello World state machine +aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ + --name "CRUDDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole" \ + --region us-east-1 \ + --definition file://../statemachine/local_testing.asl.json ``` -#### Check Execution Status +#### Debug state machine - aws cli debugging ```sh # Get state machine ARN @@ -1759,6 +234,10 @@ aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ --state-machine-arn [STATE-MACHINE-ARN] +# Start state machine manually +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] + # Check execution details aws stepfunctions describe-execution --endpoint http://localhost:8083 \ --execution-arn [STATE-MACHINE-EXECUTION-ARN] @@ -1768,18 +247,6 @@ aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ --execution-arn [STATE-MACHINE-EXECUTION-ARN] ``` -### PyTest Debugging - -For more detailed PyTest output: - -```sh -# Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG -``` - --- ## Additional Resources @@ -1789,4 +256,4 @@ python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEB - [PyTest Documentation](https://docs.pytest.org/) - [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) -[Top](#contents) +[Top](#contents) \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/tests/aws-stepfunctions-local-credentials.txt b/python-test-samples/step-functions-local-helloworld/tests/aws-stepfunctions-local-credentials.txt new file mode 100644 index 00000000..2d86952f --- /dev/null +++ b/python-test-samples/step-functions-local-helloworld/tests/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,3 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 \ No newline at end of file diff --git a/python-test-samples/step-functions-local-helloworld/tests/requirements.txt b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt index 831081ef..30cd543d 100644 --- a/python-test-samples/step-functions-local-helloworld/tests/requirements.txt +++ b/python-test-samples/step-functions-local-helloworld/tests/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.26.79 pytest>=7.2.1 pathlib -testcontainers +testcontainers \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/README.md b/python-test-samples/step-functions-local-lambda/README.md index acd2fd4b..619b031d 100644 --- a/python-test-samples/step-functions-local-lambda/README.md +++ b/python-test-samples/step-functions-local-lambda/README.md @@ -1,20 +1,20 @@ [![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) [![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) [![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) -[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) [![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) [![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) -# Local: AWS Step Functions with Lambda Integration +# AWS Step Functions: Hello World with PyTest ## Introduction -This project demonstrates how to test AWS Step Functions state machines that integrate with Lambda functions locally. It showcases a mathematical workflow that performs sum and square operations using separate Lambda functions, all tested through PyTest. +This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. + --- ## Contents -- [Local: AWS Step Functions with Lambda Integration](#local-aws-step-functions-with-lambda-integration) +- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) - [Introduction](#introduction) - [Contents](#contents) - [Architecture Overview](#architecture-overview) @@ -23,26 +23,23 @@ This project demonstrates how to test AWS Step Functions state machines that int - [Test Scenarios](#test-scenarios) - [About the Test Process](#about-the-test-process) - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - - [Common Issues](#common-issues) - [Additional Resources](#additional-resources) --- ## Architecture Overview

- AWS Step Functions with Lambda Integration + Hello World Step Functions

Components: -- Step Functions state machine for orchestration -- Two Python Lambda functions - - Sum function: adds three numbers adds three numbers (x + y + z) - - Square function: squares an entry number -- Wait state for demonstration +- Simple Step Functions state machine with a Pass state +- Testcontainers for container management +- PyTest for automated testing +- AWS Step Functions Local for local execution

- AWS Step Functions with Lambda Integration States + Hello World Step Functions - States

--- @@ -50,77 +47,58 @@ Components: ## Project Structure ``` ├── img/ -│ └── stepfunctions-lambda-states.png _# step functions Lambda state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ -├── lambda_stepfunctions_src _# folder containing source code for Step Functions Lambda functions_ +│ ├── stepfunctions-helloworld-states.png _# Step Functions state flow_ +│ └── stepfunctions-mock.png _# visual architecture diagram_ ├── statemachine/ -│ └── test/ _# folder containing valid inputs json input files for the different tests_ -│ └── local_testing.asl.json _# json file containing Lambda state machine definition_ +│ ├── local_testing.asl.json _# json file containing "Hello World" state machine definition_ +│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ ├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ └── requirements.txt _# pip requirements dependencies file_ -│ ├── README.md _# instructions file_ -│ └── template.yaml _# sam yaml template file for necessary components test_ +│ ├── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ ├── aws-stepfunctions-local-credentials.txt _# Step Functions Docker image environment vars_ +│ └── requirements.txt _# pip requirments dependencies file_ +└── README.md _# instructions file_ ``` --- ## Prerequisites - Docker -- Python 3.10 or newer (running pytest) -- AWS SAM CLI (running SAM Lambda emulator) -- AWS CLI v2 (for debugging) +- AWS cli (debugging) +- Python 3.10 or newer - Basic understanding of Step Functions -- Basic understanding of Lambda Functions +- Basic understanding of Amazon States Language (ASL) --- -## Test Scenarios - -### 1. Lambda Sum Operation -- Tests the Lambda function that adds three numbers (x, y, z) -- Verifies the correct calculation and response structure - -### 2. Lambda Square Operation -- Tests the Lambda function that squares an input number -- Verifies the correct calculation and response structure - -### 3. Step Functions Sum-Square Workflow -- Tests the full workflow execution: - 1. Sum three numbers (x + y + z) - 2. Wait for 3 seconds - 3. Square the result (sum^2) -- Verifies that all states are exited successfully -- Checks final result matches expected calculation +## Test Scenarios (end to end python test) -### 4. Large Number Handling -- Tests the workflow with larger numbers to ensure no integer overflow issues -- Uses large values for x, y, z (9999, 8888, 7777) -- Validates correct calculation of sum and square operations +### 1. Hello World +- Tests the happy path where a single state in step funtions succeed on the first attempt +- Used to validate the basic functionality of the state machine --- ## About the Test Process -The test process leverages PyTest fixtures to manage the lifecycle of the services and state machine: +The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: -1. **SAM Lambda Verification**: The `lambda_container` fixture verifies that the SAM Local Lambda emulator is running on port 3001. +1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. -2. **Step Functions Container**: The `sfn_container` fixture starts the `amazon/aws-stepfunctions-local` Docker container with host network mode to allow communication with the Lambda emulator. +2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. -3. **State Machine Creation**: The `sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. +3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. -4. **Test Execution**: Each test either: - - Directly tests a Lambda function using the Lambda client - - Executes the state machine with `execute_stepfunction` and validates the results +4. **Validation**: The test verifies that: + - The execution completed successfully + - The HelloWorld state was properly exited -5. **Cleanup**: After tests complete, the container is automatically shut down by the finalizer in the `sfn_container` fixture. +5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. --- ## Testing Workflows -### Setup Environment +### Setup Docker Environment > Make sure Docker engine is running before running the tests. @@ -129,237 +107,153 @@ step-functions-local-lambda$ docker version Client: Docker Engine - Community Version: 24.0.6 API version: 1.43 -... -``` - -To set up the Python environment: - -```shell -step-functions-local-lambda$ cd tests -$ python3 -m venv venv -$ source venv/bin/activate -$ pip install --upgrade pip -$ pip install -r requirements.txt +(...) ``` -### Start SAM Local Lambda Emulator +### Run the Unit Test - End to end python test -Start the SAM Local Lambda emulator in a separate terminal: +> Set up the python environment -```shell -step-functions-local-lambda/tests$ cd .. -$ sam local start-lambda -p 3001 --docker-network host & -``` - -### Run the Unit Tests - -```shell -step-functions-local-lambda$ cd tests +``` shell +step-functions-local-helloworld$ cd tests export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' -python3 -m pytest -s unit/src/test_step_functions_local.py -v +export AWS_REGION='us-east-1' +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt ``` -Expected output: +#### Run the unit tests +``` shell +step-functions-local-helloworld/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py ``` -$ python3 -m pytest -s unit/src/test_step_functions_local.py -v -================================================================= test session starts ================================================================= -platform linux -- Python 3.10.12, pytest-8.3.5, pluggy-1.5.0 -- /home/ubuntu/environment/step-functions-local-lambda/tests/venv/bin/python3 -cachedir: .pytest_cache -rootdir: /home/ubuntu/environment//step-functions-local-lambda/tests -plugins: xdist-3.5.0, timeout-2.3.1 -collected 4 items - -unit/src/test_step_functions_local.py::test_lambda_sum_operation Lambda sum response: {'Payload': {'result': 60}} -PASSED -unit/src/test_step_functions_local.py::test_lambda_square_operation Lambda square response: {'Payload': {'result': 81}} -PASSED -unit/src/test_step_functions_local.py::test_stepfunctions_sum_square_workflow_execution Output received: {'Payload': {'result': 289}} -PASSED -unit/src/test_step_functions_local.py::test_stepfunctions_large_number_handling Output received for large numbers: {'Payload': {'result': 710968896}} -PASSEDstepfunctions-local-test - - -================================================================= 4 passed in 16.35s ================================================================== +Expected output: +``` shell +step-functions-local-helloworld/tests$ python3 -m pytest -s unit/src/test_step_functions_local.py +========================================================== test session starts ========================================================== +platform linux -- Python 3.10.12, pytest-8.4.1, pluggy-1.6.0 +rootdir: /home/ubuntu/environment/serverless-test-samples-approach-2-pytest-distributed_Try3/step-functions-local-helloworld--OK/tests +collected 1 item + +unit/src/test_step_functions_local.py Pulling image testcontainers/ryuk:0.8.1 +Container started: ecdd9649b97d +Waiting for container with image testcontainers/ryuk:0.8.1 to be ready ... +Pulling image amazon/aws-stepfunctions-local +Container started: d1bd33969e6b +Waiting for container with image amazon/aws-stepfunctions-local to be ready ... +. + +========================================================== 1 passed in 10.57s ========================================================== +( ``` +#### Clean up section ---- - -## Debug - -### AWS CLI Commands for Manual Verification - -If you need to manually verify the state machine or execution details, you can use these commands: - -#### Configure environment +> clean pyenv environment ```sh -step-functions-local-lambda$ -export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' +step-functions-local-helloworld/tests$ +deactivate +rm -rf venv/ ``` -#### Start Lambda emulator - -```sh -step-functions-local-lambda$ -sam local start-lambda -p 3001 --docker-network host & -``` +> unsetting variables -#### Test Individually Lambda Functions ```sh -# Test Sum Lambda -aws lambda invoke \ - --function-name StepFunctionExampleSumLambda \ - --endpoint-url http://127.0.0.1:3001 \ - --payload fileb://statemachine/test/valid_input_lambda_sum.json \ - output.txt - -# Test Square Lambda -aws lambda invoke \ - --function-name StepFunctionExampleSquareLambda \ - --endpoint-url http://127.0.0.1:3001 \ - --payload fileb://statemachine/test/valid_input_lambda_square.json \ - output.txt +unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +unset AWS_REGION='us-east-1' ``` -#### Start Step Functions local: - -```sh -docker run -d --network host \ - --name stepfunctions -p 8083:8083 \ - --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local -``` +> cleanning docker -### 2. Create State Machine ```sh -aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ - --definition "{ \ - \"StartAt\": \"Lambda Sum State\", \ - \"States\": { \ - \"Lambda Sum State\": { \ - \"Next\": \"Wait State\", \ - \"Type\": \"Task\", \ - \"InputPath\": \"$\", \ - \"OutputPath\": \"$.Payload\", \ - \"Resource\": \"arn:aws:states:::lambda:invoke\", \ - \"Parameters\": { \ - \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda\",\ - \"Payload.$\": \"$\" \ - } \ - }, \ - \"Wait State\": { \ - \"Type\": \"Wait\", \ - \"Seconds\": 3, \ - \"Next\": \"Lambda Square State\" \ - }, \ - \"Lambda Square State\": { \ - \"End\": true, \ - \"Type\": \"Task\", \ - \"InputPath\": \"$.Payload\", \ - \"OutputPath\": \"$.Payload\", \ - \"Resource\": \"arn:aws:states:::lambda:invoke\", \ - \"Parameters\": { \ - \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda\",\ - \"Payload.$\": \"$\" \ - } \ - } \ - }, \ - \"TimeoutSeconds\": 300 \ - }" --name "StepFunctionsLambdaStateMachine" --role-arn "arn:aws:iam::123456789012:role/DummyRole" +docker ps --filter "ancestor=amazon/aws-stepfunctions-local" -q | xargs -r docker kill +docker rmi amazon/aws-stepfunctions-local ``` -### 3. Execute State Machine -```sh -aws stepfunctions start-execution \ - --endpoint http://localhost:8083 \ - --state-machine [STATE-MACHINE-ARN] \ - --input '{"x": 2, "y": 8, "z": 7}' -``` +#### Debug - PyTest Debugging -#### Check State Machine Execution +For more detailed debugging in pytest: -Checking state machine definition ```sh -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] -``` +# Run with verbose output +python -m pytest -s -v unit/src/test_step_functions_local.py -Checking state machine execution flow, execution and state variables -```sh -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] +# Run with debug logging +python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG ``` -Checking state machine states execution variables -```sh -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] -``` +--- -### PyTest Debugging +### Fast local development for Step Functions -For more detailed PyTest output: +#### AWS CLI Commands for Manual Verification -```sh -# Run with verbose output -python3 -m pytest -s -v unit/src/test_step_functions_local.py +If you need to manually verify the state machine or execution details, you can use these commands: -# Run with debug logging -python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +#### Configure environment variables: -# Run a specific test -python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_lambda_sum_operation +```sh +step-functions-local-helloworld/tests$ +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export AWS_REGION='us-east-1' ``` ---- - -## Common Issues +#### Set up state machine machine manually -### SAM Local Connection Issues +```sh +# Runing step functions docker image in the background +step-functions-local-helloworld/tests$ +docker run -d --network host \ + --name stepfunctions -p 8083:8083 \ + --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local +``` -If tests are skipped with "Lambda invocation failed, SAM might not be running properly": -- Ensure SAM Local is running on port 3001 -- Check that you've started SAM with `--docker-network host` -- Verify the Lambda functions are correctly defined in template.yaml -- Check SAM logs for any errors +```sh +# Creating Hello World state machine +aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ + --name "CRUDDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole" \ + --region us-east-1 \ + --definition file://../statemachine/local_testing.asl.json +``` -### Step Functions Container Fails to Start +#### Debug state machine - aws cli debugging -If the Step Functions container fails to start: -- Check if the container image "amazon/aws-stepfunctions-local" is already in use with `docker ps -a` -- Verify Docker permissions and network settings -- Ensure port 8083 is available +```sh +# Get state machine ARN +aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 -### Lambda Function Input/Output Issues +# Check state machine definition +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] -If Lambda functions aren't receiving or returning expected data: -- Check that the input JSON files have the correct structure -- For the sum function, ensure it has "x", "y", and "z" fields -- For the square function, ensure it has a "result" field -- Verify the Lambda handler in app.py correctly processes the input +# Start state machine manually +aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] -### Step Functions Execution Failure +# Check execution details +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] -If Step Functions executions fail: -- Check the execution history with the AWS CLI -- Verify the Lambda resource ARNs in the state machine definition -- Ensure the Step Functions container's LAMBDA_ENDPOINT points to the correct SAM Local port +# Check execution history +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` --- ## Additional Resources - [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) -- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) -- [AWS Lambda Local Testing](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) -- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) - [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) +- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) - [PyTest Documentation](https://docs.pytest.org/) -- -[Top](#contents) +- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) +[Top](#contents) \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json b/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json index fa3982d8..4b0867f7 100644 --- a/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json +++ b/python-test-samples/step-functions-local-lambda/statemachine/local_testing.asl.json @@ -1,34 +1,10 @@ -{ - "Comment": "This state machine is called: MathOperationsStateMachine", - "StartAt": "Lambda Sum State", +{ + "Comment": "Hello World State Machine of the Amazon States Language using a Pass state", + "StartAt": "HelloWorld", "States": { - "Lambda Sum State": { - "Type": "Task", - "Resource": "arn:aws:states:::lambda:invoke", - "Parameters": { - "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda", - "Payload.$": "$" - }, - "InputPath": "$", - "OutputPath": "$.Payload", - "Next": "Wait State" - }, - "Wait State": { - "Type": "Wait", - "Seconds": 3, - "Next": "Lambda Square State" - }, - "Lambda Square State": { - "Type": "Task", - "Resource": "arn:aws:states:::lambda:invoke", - "Parameters": { - "FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda", - "Payload.$": "$" - }, - "InputPath": "$.Payload", - "OutputPath": "$.Payload", + "HelloWorld": { + "Type": "Pass", "End": true } - }, - "TimeoutSeconds": 300 -} + } +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input.json b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input.json new file mode 100644 index 00000000..6a6ec7a7 --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/statemachine/test/valid_input.json @@ -0,0 +1,6 @@ +{ + "data": { + }, + "comments": "" + } +} \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/tests/aws-stepfunctions-local-credentials.txt b/python-test-samples/step-functions-local-lambda/tests/aws-stepfunctions-local-credentials.txt new file mode 100644 index 00000000..2d86952f --- /dev/null +++ b/python-test-samples/step-functions-local-lambda/tests/aws-stepfunctions-local-credentials.txt @@ -0,0 +1,3 @@ +AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE +AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY +AWS_DEFAULT_REGION=us-east-1 \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/tests/requirements.txt b/python-test-samples/step-functions-local-lambda/tests/requirements.txt index b82bf3b5..30cd543d 100644 --- a/python-test-samples/step-functions-local-lambda/tests/requirements.txt +++ b/python-test-samples/step-functions-local-lambda/tests/requirements.txt @@ -1,8 +1,4 @@ -pytest==8.3.5 -boto3==1.34.25 -testcontainers==3.7.1 -botocore==1.34.25 -pytest-timeout==2.3.1 -pytest-xdist==3.5.0 -python-dotenv==1.0.1 -requests>=2.31.0 \ No newline at end of file +boto3>=1.26.79 +pytest>=7.2.1 +pathlib +testcontainers \ No newline at end of file diff --git a/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py b/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py index 6e10fbdb..ae4a7293 100644 --- a/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py +++ b/python-test-samples/step-functions-local-lambda/tests/unit/src/test_step_functions_local.py @@ -1,111 +1,53 @@ -"""Tests Step Functions local with Lambda integrations using pytest""" +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +"""Tests Step Functions local with mocks using pytest""" import os import time -import json import logging -import subprocess from pathlib import Path import pytest import boto3 -import requests from botocore.exceptions import ClientError from testcontainers.core.container import DockerContainer from testcontainers.core.waiting_utils import wait_for_logs log = logging.getLogger() -SAM_LAMBDA_PORT = 3001 - -@pytest.fixture(name="lambda_container", scope="session") -def fixture_lambda_container(request): +@pytest.fixture(name="container", scope="session") +def fixture_container(request): """ - Verifies SAM local Lambda emulator is running + Runs the amazon/aws-stepfunctions-local container without the MockConfigFile """ - # Try to connect using requests - try: - response = requests.get(f"http://127.0.0.1:{SAM_LAMBDA_PORT}/2018-06-01/ping", timeout=2) - if response.status_code == 200: - log.info(f"SAM Local is available at port {SAM_LAMBDA_PORT}") - else: - log.warning(f"SAM Local responded with status code {response.status_code}") - except subprocess.CalledProcessError: - log.warning(f"SAM Local not detected at port {SAM_LAMBDA_PORT}. Tests may fail.") + sf_local = DockerContainer("amazon/aws-stepfunctions-local") \ + .with_bind_ports(8083, 8083) \ + .with_exposed_ports(8083) - yield None + # Start the container + sf_local.start() + wait_for_logs(sf_local, "Starting server on port 8083") + # Important to avoid non-deterministic behavior, waiting for container to spin up + time.sleep(2) -@pytest.fixture(name="sfn_container", scope="session") -def fixture_sfn_container(request, lambda_container): - """ - Runs the amazon/aws-stepfunctions-local container - - Host network is required for proper SAM Lambda integration as it allows the - Step Functions container to directly access the Lambda emulator on the host. - We use subprocess to run Docker commands directly since it provides more - reliable control over host networking than testcontainers Python Docker libraries. - - """ - # Get Docker command directly - testcontainers doesn't support host network mode - container_name = "stepfunctions-local-test" - - # Stop previous container if exists - try: - subprocess.run(["docker", "rm", "-f", container_name], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False) - except Exception: - pass - - # Start container with host network - cmd = [ - "docker", "run", "--rm", "-d", - "--name", container_name, - "--network", "host", - "-e", "AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE", - "-e", "AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY", - "-e", "AWS_DEFAULT_REGION=us-east-1", - "-e", f"LAMBDA_ENDPOINT=http://127.0.0.1:{SAM_LAMBDA_PORT}", - "amazon/aws-stepfunctions-local" - ] - - container_id = subprocess.check_output(cmd).decode().strip() - log.info(f"Started Step Functions container: {container_id}") - - # Wait for container to be ready - time.sleep(5) - - # Check if container is running - result = subprocess.run( - ["docker", "logs", container_name], - capture_output=True, - text=True - ) - if "Starting server on port 8083" not in result.stdout: - log.warning("Step Functions container might not be ready. Check container logs.") - time.sleep(5) - - # Create client - client = boto3.client('stepfunctions', - endpoint_url='http://localhost:8083', - aws_access_key_id="DUMMYIDEXAMPLE", - aws_secret_access_key="DUMMYEXAMPLEKEY", - region_name="us-east-1") - def stop_step_function(): - log.info(f"[fixture] stopping step functions container {container_name}") - subprocess.run(["docker", "stop", container_name], check=False) - + log.info("[fixture] stopping step functions container") + sf_local.stop() + request.addfinalizer(stop_step_function) - - return {'client': client, 'container_id': container_id, 'container_name': container_name} + return sf_local @pytest.fixture(name="sfn_client", scope="session", autouse=True) -def fixture_sfn_client(sfn_container): +def fixture_sfn_client(container): """ - Creates the state machine for Lambda integration testing + Creates the state machine using the local_testing.asl.json definition """ - # Get the client from sfn_container - client = sfn_container['client'] + + # Set up Step Function client with test container URL + client = boto3.client('stepfunctions', + endpoint_url='http://' + container.get_container_host_ip() + ':' + + container.get_exposed_port(8083)) # Read state machine definition step_function_definition = Path( @@ -115,7 +57,7 @@ def fixture_sfn_client(sfn_container): # Create state machine try: client.create_state_machine( - name="LocalTesting", + name="HelloWorldStateMachine", definition=step_function_definition, roleArn="arn:aws:iam::123456789012:role/DummyRole" ) @@ -126,7 +68,7 @@ def fixture_sfn_client(sfn_container): err.response["Error"]["Message"], ) raise - + return client @@ -135,22 +77,27 @@ def get_arn(sfn_client): Get state machine ARN """ state_machine_arn = sfn_client.list_state_machines()["stateMachines"][0]["stateMachineArn"] - return state_machine_arn + return state_machine_arn -def execute_stepfunction(sfn_client, execution_name, input_data, max_wait_seconds=30): +def execute_stepfunction(sfn_client, execution_name, test_name=None): """ - Executes the step function with the provided input + Executes the step function with an empty input Returns a history of the state transitions once the step function is complete + + @param: test_name - No longer used with simple Hello World example """ state_machine_arn = get_arn(sfn_client) + # Empty input is sufficient for Hello World Pass state + step_function_input = "{}" + try: # Starting execution of the state machine start_execution = sfn_client.start_execution( name=execution_name, - stateMachineArn=state_machine_arn, - input=json.dumps(input_data) + stateMachineArn=state_machine_arn, # No longer appending test_name + input=step_function_input ) except ClientError as err: log.error( @@ -161,214 +108,60 @@ def execute_stepfunction(sfn_client, execution_name, input_data, max_wait_second ) raise - start_time = time.time() - while time.time() - start_time < max_wait_seconds: + while True: time.sleep(1) # Checking whether the execution has completed. try: - execution_response = sfn_client.describe_execution( + history_response = sfn_client.get_execution_history( executionArn=start_execution['executionArn']) - - if execution_response['status'] in ['SUCCEEDED', 'FAILED', 'TIMED_OUT', 'ABORTED']: - # Get the execution history - history_response = sfn_client.get_execution_history( - executionArn=start_execution['executionArn']) - - # Log details if execution failed - if execution_response['status'] != 'SUCCEEDED': - log.error(f"Execution failed with status: {execution_response['status']}") - if 'error' in execution_response: - log.error(f"Error: {execution_response.get('error')}") - if 'cause' in execution_response: - log.error(f"Cause: {execution_response.get('cause')}") - - return history_response, execution_response - except ClientError as err: log.error( - "Couldn't fetch execution details for state machine %s. Here's why: %s: %s", + "Couldn't fetch execution history for state machine %s. Here's why: %s: %s", state_machine_arn, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise - raise TimeoutError(f"Execution did not complete within {max_wait_seconds} seconds") - - -def check_state_exited_event_details(history_response, state_name): - """ - Test utility - used to verify which state the task exited in - """ - success = False + success = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + success = True + break - for event in history_response['events']: - # Check for regular Task state exits - if event['type'] == 'TaskStateExited' and 'stateExitedEventDetails' in event and \ - event['stateExitedEventDetails']['name'] == state_name: - success = True - break - - # Check for Wait state exits - they use a different event type - if event['type'] == 'WaitStateExited' and 'stateExitedEventDetails' in event and \ - event['stateExitedEventDetails']['name'] == state_name: - success = True - break - - # Generic check for any state exit - if 'stateExitedEventDetails' in event and \ - event['stateExitedEventDetails']['name'] == state_name: - success = True + if success: break - return success + return history_response -def test_lambda_sum_operation(sfn_client): - """ - Test just the Lambda sum operation is working correctly - """ - - # Load JSON input file and parse it - input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', - 'valid_input_lambda_sum.json')).read_text(encoding="utf-8") - input_data = json.loads(input_data_str) # Parse the JSON string into a dictionary - - # Expected sum x + y + z = 10 + 20 + 30 = 60 - expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 60 - - # Create a lambda client to directly test the function - lambda_client = boto3.client('lambda', - endpoint_url=f'http://127.0.0.1:{SAM_LAMBDA_PORT}', - aws_access_key_id="DUMMYIDEXAMPLE", - aws_secret_access_key="DUMMYEXAMPLEKEY", - region_name="us-east-1") - - # Test sum lambda directly - try: - response = lambda_client.invoke( - FunctionName='StepFunctionExampleSumLambda', - Payload=json.dumps(input_data) - ) - - # Parse response - result = json.loads(response['Payload'].read()) - print(f"Lambda sum response: {result}") - - # Check result based on the actual structure - assert 'Payload' in result, "Response missing 'Payload' field" - assert 'result' in result['Payload'], "Payload missing 'result' field" - assert result['Payload']['result'] == expected_sum, f"Expected sum {expected_sum}, got {result['Payload']['result']}" - except Exception as e: - pytest.skip(f"Lambda invocation failed, SAM might not be running properly: {e}") - - -def test_lambda_square_operation(sfn_client): +def check_state_exited_event_details(history_response, state_name): """ - Test just the Lambda square operation is working correctly + Test utility - checks if a state with the given name was exited during execution + Works with any state type (Pass, Task, etc.) """ + for event in history_response['events']: + if 'stateExitedEventDetails' in event and event['stateExitedEventDetails']['name'] == state_name: + return True - # Load JSON input file and parse it - input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', - 'valid_input_lambda_square.json')).read_text(encoding="utf-8") - input_data = json.loads(input_data_str) - - # Expected 9^2 = 9 * 9 = 81 - expected_square = input_data["result"] * input_data["result"] # 81 - - # Create a lambda client to directly test the function - lambda_client = boto3.client('lambda', - endpoint_url=f'http://127.0.0.1:{SAM_LAMBDA_PORT}', - aws_access_key_id="DUMMYIDEXAMPLE", - aws_secret_access_key="DUMMYEXAMPLEKEY", - region_name="us-east-1") - - # Test square lambda directly - try: - response = lambda_client.invoke( - FunctionName='StepFunctionExampleSquareLambda', - Payload=json.dumps(input_data) - ) - - # Parse response - payload_bytes = response['Payload'].read() - result = json.loads(payload_bytes) - print(f"Lambda square response: {result}") - - # Check result based on the actual structure - assert 'Payload' in result, "Response missing 'Payload' field" - assert 'result' in result['Payload'], "Payload missing 'result' field" - assert result['Payload']['result'] == expected_square, f"Expected square {expected_square}, got {result['Payload']['result']}" - except Exception as e: - pytest.skip(f"Lambda invocation failed, SAM might not be running properly: {e}") - + return False -def test_stepfunctions_sum_square_workflow_execution(sfn_client): +def test_happy_path(sfn_client): """ - Test full execution of the mathematical workflow: - 1. Sum three numbers (x + y + z) - 2. Wait for 3 seconds - 3. Square the result (sum^2) + Testing that the hello world step function completes successfully. + Verifies that the state machine exits from the HelloWorld state. """ - - # Load JSON input file and parse it - input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', - 'valid_input_stepfunctions_sum_square.json')).read_text(encoding="utf-8") - input_data = json.loads(input_data_str) - - # Expected sum and final result - expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 17 - expected_final = expected_sum * expected_sum # 289 - - # Execute the state machine - history_response, execution_response = execute_stepfunction( - sfn_client, 'mathWorkflowExecution', input_data) - - # Check execution succeeded - assert execution_response['status'] == 'SUCCEEDED', "Execution did not succeed" - - # Verify output contains the expected result - output = json.loads(execution_response['output']) - print(f"Output received: {output}") - - # Check for the result in the Payload structure - assert 'Payload' in output, "Output missing 'Payload' field" - assert 'result' in output['Payload'], "Payload missing 'result' field" - assert output['Payload']['result'] == expected_final, f"Expected result {expected_final}, got {output['Payload']['result']}" - - # Check that all states exited successfully - assert check_state_exited_event_details(history_response, 'Lambda Sum State') - assert check_state_exited_event_details(history_response, 'Wait State') - assert check_state_exited_event_details(history_response, 'Lambda Square State') - + history_response = execute_stepfunction(sfn_client, 'happyPathExecution') -def test_stepfunctions_large_number_handling(sfn_client): - """ - Test handling of larger numbers to ensure no integer overflow issues - """ - - # Load JSON input file and parse it - input_data_str = Path(os.path.join(os.path.dirname(__file__), '../../..', 'statemachine', 'test', - 'valid_input_stepfunctions_large_numbers.json')).read_text(encoding="utf-8") - input_data = json.loads(input_data_str) - - # Expected results - expected_sum = input_data["x"] + input_data["y"] + input_data["z"] # 26664 - expected_final = expected_sum * expected_sum # 710968896 - - # Execute the state machine - history_response, execution_response = execute_stepfunction( - sfn_client, 'largeNumberExecution', input_data) - - # Check execution succeeded - assert execution_response['status'] == 'SUCCEEDED', "Execution did not succeed" + # Check if execution succeeded + execution_succeeded = False + for event in history_response['events']: + if event['type'] == 'ExecutionSucceeded': + execution_succeeded = True + break - # Verify output contains the expected result - output = json.loads(execution_response['output']) - print(f"Output received for large numbers: {output}") + assert execution_succeeded, "Step function execution did not succeed" - # Check for the result in the Payload structure - assert 'Payload' in output, "Output missing 'Payload' field" - assert 'result' in output['Payload'], "Payload missing 'result' field" - assert output['Payload']['result'] == expected_final, f"Expected result {expected_final}, got {output['Payload']['result']}" \ No newline at end of file + # Check if HelloWorld state was exited + assert check_state_exited_event_details(history_response, 'HelloWorld'), "HelloWorld state was not exited" \ No newline at end of file diff --git a/python-test-samples/step-functions-local-mock/README.md b/python-test-samples/step-functions-local-mock/README.md index a7d50954..3c9b8c92 100644 --- a/python-test-samples/step-functions-local-mock/README.md +++ b/python-test-samples/step-functions-local-mock/README.md @@ -23,7 +23,6 @@ This project demonstrates how to test AWS Step Functions workflows locally using - [Prerequisites](#prerequisites) - [Test Scenarios](#test-scenarios) - [Testing Workflows](#testing-workflows) - - [Debug](#debug) - [Additional Resources](#additional-resources) --- @@ -48,14 +47,15 @@ Components: ## Project Structure ``` ├── img/ -│ └── stepfunctions-mock-states.png _# step functions MOCK state flow_ +│ ├── stepfunctions-mock-states.png _# step functions MOCK state flow_ │ └── stepfunctions-mock.png _# visual architecture diagram_ ├── statemachine/ -│ └── test/MockConfigFile.json _# json file defining MOCK answers from Step Funtions state machine_ +│ ├── test/MockConfigFile.json _# json file defining MOCK answers from Step Funtions state machine_ │ └── local_testing.asl.json _# json file containing MOCK state machine definition_ ├── tests/ -│ └── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ ├── unit/src/test_step_functions_local.py _# python PyTest test definition_ │ └── requirements.txt _# pip requirements dependencies file_ +├── aws-stepfunctions-local-credentials.txt _# Step Functions Docker image environment vars_ └── README.md _# instructions file_ ``` @@ -92,6 +92,8 @@ Components: ## Testing Workflows +### Setup Docker Environment + > Make sure docker engine is running before running the tests. ``` shell @@ -101,7 +103,9 @@ Client: Docker Engine - Community API version: 1.43 ``` -To set it up: +### Run the Unit Test - End to end python test + +> Set up the python environment: ``` shell step-functions-local-mock$ cd tests @@ -111,17 +115,18 @@ pip install --upgrade pip pip install -r requirements.txt ``` -To run the unit tests: +#### Run the Unit Tests ``` shell -step-functions-local-mock$ cd tests -python3 -m pytest -s unit/src/test_step_functions_local.py -v +step-functions-local-mock/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py ``` -expected output +Expected output -``` -step-functions-local-mock$ python3 -m pytest -s unit/src/test_step_functions_local.py -v +``` shell +step-functions-local-mock/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py ============================================================== test session starts ============================================================== platform linux -- Python 3.10.12, pytest-8.3.5, pluggy-1.5.0 -- /home/ubuntu/environment/step-functions-local-mock/tests/venv/bin/python3 cachedir: .pytest_cache @@ -133,34 +138,84 @@ Container started: 86676d42c5de Waiting for container with image testcontainers/ryuk:0.8.1 to be ready ... Pulling image amazon/aws-stepfunctions-local Container started: 034b00a0307a -Waiting for container with image amazon/aws-stepfunctions-local to be ready ... +Waiting for container with image amazon/aws-stepfunctions-local to be ready ... PASSED +unit/src/test_step_functions_local.py::test_retry_path PASSED +unit/src/test_step_functions_local.py::test_hybrid_path PASSED + +======================================================================== 3 passed in 25.94s ========================================================================= ``` ---- -## Debug +#### Clean up section -### AWS CLI Commands for Manual Testing +> clean pyenv environment -#### 1. Start the Step Functions Local container: +```sh +step-functions-local-helloworld/tests$ +deactivate +rm -rf venv/ +``` + +> unsetting variables ```sh -docker run -d -p 8083:8083 \ - --mount type=bind,readonly,source=$(pwd)/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ - --env-file aws-stepfunctions-local-credentials.txt \ - amazon/aws-stepfunctions-local +unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +unset AWS_REGION='us-east-1' +``` + +> cleanning docker + +```sh +docker ps --filter "ancestor=amazon/aws-stepfunctions-local" -q | xargs -r docker kill +docker rmi amazon/aws-stepfunctions-local +``` + +#### Debug - PyTest Debugging + +For more detailed debugging in pytest: + +```sh +# Run with verbose output and show print statements +python3 -m pytest -s -v unit/src/test_step_functions_local.py + +# Run with debug logging +python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG + +# Run only a specific test +python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_hybrid_path +python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_retry_path ``` -#### 2. Set up environment variables: +--- + +### Fast local development for Step Functions + +#### AWS CLI Commands for Manual Verification + +If you need to manually verify the state machine or execution details, you can use these commands: + +#### Configure environment variables: ```sh +step-functions-local-lambda$ export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -export REGION='us-east-1' +export AWS_REGION='us-east-1' ``` -#### 3. Create the state machine: +#### Debug state machine - 1. Start the Step Functions Local container: + +```sh +step-functions-local-mock$ +docker run -d -p 8083:8083 \ + --mount type=bind,readonly,source=$(pwd)/statemachine/test/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ + --env-file aws-stepfunctions-local-credentials.txt \ + amazon/aws-stepfunctions-local +``` + +#### Debug state machine - 2. Create the state machine: ```sh aws stepfunctions create-state-machine \ @@ -170,7 +225,7 @@ aws stepfunctions create-state-machine \ --role-arn "arn:aws:iam::123456789012:role/DummyRole" ``` -#### 4. Execute test scenarios: +#### Debug state machine - 3. Execute test scenarios: Happy Path: ```sh @@ -193,7 +248,7 @@ aws stepfunctions start-execution \ --state-machine "arn:aws:states:us-east-1:123456789012:stateMachine:LambdaSQSIntegration#HybridPath" ``` -#### 5. Inspect execution results: +#### Debug state machines - 4. Inspect execution results: Checking state machine execution ```sh @@ -202,6 +257,11 @@ aws stepfunctions describe-execution \ --execution-arn "" ``` +#### Debug state machines - 5. Checking docker image logs +```sh +docker ps --filter "ancestor=amazon/aws-stepfunctions-local" -q | xargs docker logs +``` + Checking state machine definition ```sh aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ @@ -220,21 +280,6 @@ aws stepfunctions get-execution-history --endpoint http://localhost:8083 --execution-arn [STATE-MACHINE-EXECUTION-ARN] ``` -### PyTest Debugging - -For more detailed debugging in pytest: - -```sh -# Run with verbose output and show print statements -python3 -m pytest -s -v unit/src/test_step_functions_local.py - -# Run with debug logging -python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG - -# Run only a specific test -python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_hybrid_path -``` - --- ## Additional Resources diff --git a/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt b/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt index 744075e4..4baf5575 100644 --- a/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt +++ b/python-test-samples/step-functions-local-mock/aws-stepfunctions-local-credentials.txt @@ -2,3 +2,4 @@ AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE AWS_SECRET_ACCESS_KEY=DUMMYEXAMPLEKEY AWS_DEFAULT_REGION=us-east-1 LAMBDA_ENDPOINT=http://127.0.0.1:3001 +SFN_MOCK_CONFIG=/home/StepFunctionsLocal/MockConfigFile.json \ No newline at end of file diff --git a/python-test-samples/step-functions-local-mock/tests/requirements.txt b/python-test-samples/step-functions-local-mock/tests/requirements.txt index cdb0c683..1bf41e7e 100644 --- a/python-test-samples/step-functions-local-mock/tests/requirements.txt +++ b/python-test-samples/step-functions-local-mock/tests/requirements.txt @@ -1,4 +1,4 @@ boto3>=1.38.4 pytest>=7.2.1 pathlib -testcontainers +testcontainers \ No newline at end of file From dfeeb04e89a6fa977070a98150991fecb61d7673 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 23 Jul 2025 11:00:14 +0000 Subject: [PATCH 7/7] Deleting network host references and path fixes as per PR comments --- .../step-functions-local-helloworld/README.md | 10 +- .../step-functions-local-lambda/README.md | 323 +++++++++++++----- .../step-functions-local-mock/README.md | 19 +- 3 files changed, 254 insertions(+), 98 deletions(-) diff --git a/python-test-samples/step-functions-local-helloworld/README.md b/python-test-samples/step-functions-local-helloworld/README.md index 619b031d..8d714df7 100644 --- a/python-test-samples/step-functions-local-helloworld/README.md +++ b/python-test-samples/step-functions-local-helloworld/README.md @@ -165,9 +165,9 @@ rm -rf venv/ > unsetting variables ```sh -unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -unset AWS_REGION='us-east-1' +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset AWS_REGION ``` > cleanning docker @@ -211,7 +211,7 @@ export AWS_REGION='us-east-1' ```sh # Runing step functions docker image in the background step-functions-local-helloworld/tests$ -docker run -d --network host \ +docker run -d \ --name stepfunctions -p 8083:8083 \ --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local ``` @@ -256,4 +256,4 @@ aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - [PyTest Documentation](https://docs.pytest.org/) - [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) -[Top](#contents) \ No newline at end of file +[Top](#contents) diff --git a/python-test-samples/step-functions-local-lambda/README.md b/python-test-samples/step-functions-local-lambda/README.md index 619b031d..f60b5ec0 100644 --- a/python-test-samples/step-functions-local-lambda/README.md +++ b/python-test-samples/step-functions-local-lambda/README.md @@ -1,20 +1,20 @@ [![python: 3.10](https://img.shields.io/badge/Python-3.10-green)](https://img.shields.io/badge/Python-3.10-green) [![AWS: Step Functions](https://img.shields.io/badge/AWS-Step%20Functions-orange)](https://img.shields.io/badge/AWS-Step%20Functions-orange) [![Language: ASL](https://img.shields.io/badge/Language-ASL-blue)](https://img.shields.io/badge/Language-ASL-blue) +[![AWS: Lambda](https://img.shields.io/badge/AWS-Lambda-yellow)](https://img.shields.io/badge/AWS-Lambda-yellow) [![test: pytest](https://img.shields.io/badge/Test-Pytest-red)](https://img.shields.io/badge/Test-Pytest-red) [![test: local](https://img.shields.io/badge/Test-Local-red)](https://img.shields.io/badge/Test-Local-red) -# AWS Step Functions: Hello World with PyTest +# Local: AWS Step Functions with Lambda Integration ## Introduction -This project demonstrates how to test AWS Step Functions state machines locally using Docker and PyTest. It focuses on a simple "Hello World" state machine with a Pass state, showing how to create and validate automated tests for AWS Step Functions locally. - +This project demonstrates how to test AWS Step Functions state machines that integrate with Lambda functions locally. It showcases a mathematical workflow that performs sum and square operations using separate Lambda functions, all tested through PyTest. --- ## Contents -- [AWS Step Functions: Hello World with PyTest](#aws-step-functions-hello-world-with-pytest) +- [Local: AWS Step Functions with Lambda Integration](#local-aws-step-functions-with-lambda-integration) - [Introduction](#introduction) - [Contents](#contents) - [Architecture Overview](#architecture-overview) @@ -23,23 +23,25 @@ This project demonstrates how to test AWS Step Functions state machines locally - [Test Scenarios](#test-scenarios) - [About the Test Process](#about-the-test-process) - [Testing Workflows](#testing-workflows) + - [Common Issues](#common-issues) - [Additional Resources](#additional-resources) --- ## Architecture Overview

- Hello World Step Functions + AWS Step Functions with Lambda Integration

Components: -- Simple Step Functions state machine with a Pass state -- Testcontainers for container management -- PyTest for automated testing -- AWS Step Functions Local for local execution +- Step Functions state machine for orchestration +- Two Python Lambda functions + - Sum function: adds three numbers adds three numbers (x + y + z) + - Square function: squares an entry number +- Wait state for demonstration

- Hello World Step Functions - States + AWS Step Functions with Lambda Integration States

--- @@ -47,52 +49,72 @@ Components: ## Project Structure ``` ├── img/ -│ ├── stepfunctions-helloworld-states.png _# Step Functions state flow_ -│ └── stepfunctions-mock.png _# visual architecture diagram_ +│ ├── stepfunctions-lambda-states.png _# step functions Lambda state flow_ +│ └── stepfunctions-lambda.png _# visual architecture diagram_ +├── lambda_stepfunctions_src _# folder containing source code for Step Functions Lambda functions_ ├── statemachine/ -│ ├── local_testing.asl.json _# json file containing "Hello World" state machine definition_ -│ └── test/valid_input.json _# json file containing "Hello World" state machine input_ +│ ├── test/ _# folder containing valid inputs json input files for the different tests_ +│ └── local_testing.asl.json _# json file containing Lambda state machine definition_ ├── tests/ -│ ├── unit/src/test_step_functions_local.py _# python PyTest test definition_ -│ ├── aws-stepfunctions-local-credentials.txt _# Step Functions Docker image environment vars_ -│ └── requirements.txt _# pip requirments dependencies file_ -└── README.md _# instructions file_ +│ ├── unit/src/test_step_functions_local.py _# python PyTest test definition_ +│ └── requirements.txt _# pip requirements dependencies file_ +├── template.yaml _# sam yaml template file for necessary components test_ +├── aws-stepfunctions-local-credentials.txt _# Step Functions Docker image environment vars_ +└── README.md _# instructions file_ ``` --- ## Prerequisites - Docker -- AWS cli (debugging) -- Python 3.10 or newer +- Python 3.10 or newer (running pytest) +- AWS SAM CLI (running SAM Lambda emulator) +- AWS CLI v2 (for debugging) - Basic understanding of Step Functions -- Basic understanding of Amazon States Language (ASL) +- Basic understanding of Lambda Functions --- -## Test Scenarios (end to end python test) +## Test Scenarios + +### 1. Lambda Sum Operation +- Tests the Lambda function that adds three numbers (x, y, z) +- Verifies the correct calculation and response structure + +### 2. Lambda Square Operation +- Tests the Lambda function that squares an input number +- Verifies the correct calculation and response structure + +### 3. Step Functions Sum-Square Workflow +- Tests the full workflow execution: + 1. Sum three numbers (x + y + z) + 2. Wait for 3 seconds + 3. Square the result (sum^2) +- Verifies that all states are exited successfully +- Checks final result matches expected calculation -### 1. Hello World -- Tests the happy path where a single state in step funtions succeed on the first attempt -- Used to validate the basic functionality of the state machine +### 4. Large Number Handling +- Tests the workflow with larger numbers to ensure no integer overflow issues +- Uses large values for x, y, z (9999, 8888, 7777) +- Validates correct calculation of sum and square operations --- ## About the Test Process -The test process leverages PyTest fixtures to manage the lifecycle of the Step Functions Local container and the state machine: +The test process leverages PyTest fixtures to manage the lifecycle of the services and state machine: -1. **Container Setup**: The `fixture_container` fixture downloads and starts the `amazon/aws-stepfunctions-local` container. +1. **SAM Lambda Verification**: The `lambda_container` fixture verifies that the SAM Local Lambda emulator is running on port 3001. -2. **State Machine Creation**: The `fixture_sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. +2. **Step Functions Container**: The `sfn_container` fixture starts the `amazon/aws-stepfunctions-local` Docker container with host network mode to allow communication with the Lambda emulator. -3. **Test Execution**: When the test runs, it calls `execute_stepfunction` to start an execution of the state machine and monitor it until completion. +3. **State Machine Creation**: The `sfn_client` fixture creates a Boto3 client for Step Functions and creates the state machine using the definition from `local_testing.asl.json`. -4. **Validation**: The test verifies that: - - The execution completed successfully - - The HelloWorld state was properly exited +4. **Test Execution**: Each test either: + - Directly tests a Lambda function using the Lambda client + - Executes the state machine with `execute_stepfunction` and validates the results -5. **Cleanup**: After the test completes, the container is automatically shut down by the finalizer added in the `fixture_container`. +5. **Cleanup**: After tests complete, the container is automatically shut down by the finalizer in the `sfn_container` fixture. --- @@ -112,10 +134,17 @@ Client: Docker Engine - Community ### Run the Unit Test - End to end python test -> Set up the python environment +> Start the SAM Local Lambda emulator in a separate terminal: + +```shell +step-functions-local-lambda$ +sam local start-lambda -p 3001 & +``` -``` shell -step-functions-local-helloworld$ cd tests +> Set up the python environment: + +```shell +step-functions-local-lambda$ cd tests export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' export AWS_REGION='us-east-1' @@ -125,31 +154,37 @@ pip install --upgrade pip pip install -r requirements.txt ``` -#### Run the unit tests +#### Run the Unit Tests -``` shell -step-functions-local-helloworld/tests$ +```shell +step-functions-local-lambda/tests$ python3 -m pytest -s unit/src/test_step_functions_local.py ``` Expected output: -``` shell -step-functions-local-helloworld/tests$ python3 -m pytest -s unit/src/test_step_functions_local.py -========================================================== test session starts ========================================================== -platform linux -- Python 3.10.12, pytest-8.4.1, pluggy-1.6.0 -rootdir: /home/ubuntu/environment/serverless-test-samples-approach-2-pytest-distributed_Try3/step-functions-local-helloworld--OK/tests -collected 1 item - -unit/src/test_step_functions_local.py Pulling image testcontainers/ryuk:0.8.1 -Container started: ecdd9649b97d -Waiting for container with image testcontainers/ryuk:0.8.1 to be ready ... -Pulling image amazon/aws-stepfunctions-local -Container started: d1bd33969e6b -Waiting for container with image amazon/aws-stepfunctions-local to be ready ... -. - -========================================================== 1 passed in 10.57s ========================================================== -( + +``` +step-functions-local-lambda/tests$ +python3 -m pytest -s unit/src/test_step_functions_local.py +================================================================= test session starts ================================================================= +platform linux -- Python 3.10.12, pytest-8.3.5, pluggy-1.5.0 -- /home/ubuntu/environment/step-functions-local-lambda/tests/venv/bin/python3 +cachedir: .pytest_cache +rootdir: /home/ubuntu/environment//step-functions-local-lambda/tests +plugins: xdist-3.5.0, timeout-2.3.1 +collected 4 items + +unit/src/test_step_functions_local.py::test_lambda_sum_operation Lambda sum response: {'Payload': {'result': 60}} +PASSED +unit/src/test_step_functions_local.py::test_lambda_square_operation Lambda square response: {'Payload': {'result': 81}} +PASSED +unit/src/test_step_functions_local.py::test_stepfunctions_sum_square_workflow_execution Output received: {'Payload': {'result': 289}} +PASSED +unit/src/test_step_functions_local.py::test_stepfunctions_large_number_handling Output received for large numbers: {'Payload': {'result': 710968896}} +PASSEDstepfunctions-local-test + + +================================================================= 4 passed in 16.35s ================================================================== + ``` #### Clean up section @@ -157,7 +192,7 @@ Waiting for container with image amazon/aws-stepfuncti > clean pyenv environment ```sh -step-functions-local-helloworld/tests$ +step-functions-local-lambda/tests$ deactivate rm -rf venv/ ``` @@ -165,9 +200,15 @@ rm -rf venv/ > unsetting variables ```sh -unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -unset AWS_REGION='us-east-1' +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset AWS_REGION +``` + +> cleaning sam process + +```sh +ps -axuf | grep '[s]am local start-lambda' | awk '{print $2}' | xargs -r kill ``` > cleanning docker @@ -183,10 +224,13 @@ For more detailed debugging in pytest: ```sh # Run with verbose output -python -m pytest -s -v unit/src/test_step_functions_local.py +python3 -m pytest -s -v unit/src/test_step_functions_local.py # Run with debug logging -python -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG +python3 -m pytest -s -v unit/src/test_step_functions_local.py --log-cli-level=DEBUG + +# Run a specific pytest test +python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_lambda_sum_operation ``` --- @@ -200,60 +244,159 @@ If you need to manually verify the state machine or execution details, you can u #### Configure environment variables: ```sh -step-functions-local-helloworld/tests$ +step-functions-local-lambda$ export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' export AWS_REGION='us-east-1' ``` -#### Set up state machine machine manually +#### Start Lambda emulator + +```sh +step-functions-local-lambda$ +sam local start-lambda -p 3001 & +``` +#### Debug lambda functions - Test Individually Lambda Functions ```sh -# Runing step functions docker image in the background -step-functions-local-helloworld/tests$ -docker run -d --network host \ +# Test Sum Lambda +step-functions-local-lambda$ +aws lambda invoke \ + --function-name StepFunctionExampleSumLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload fileb://statemachine/test/valid_input_lambda_sum.json \ + output.txt +cat output.txt + +# Test Square Lambda +step-functions-local-lambda$ +aws lambda invoke \ + --function-name StepFunctionExampleSquareLambda \ + --endpoint-url http://127.0.0.1:3001 \ + --payload fileb://statemachine/test/valid_input_lambda_square.json \ + output.txt +cat output.txt +``` + +#### Debug state machine - 1. Start Step Functions local image: + +```sh +docker run -d \ --name stepfunctions -p 8083:8083 \ --env-file aws-stepfunctions-local-credentials.txt amazon/aws-stepfunctions-local ``` +#### Debug state machine - 2. Create State Machine ```sh -# Creating Hello World state machine -aws stepfunctions create-state-machine --endpoint-url http://localhost:8083 \ - --name "CRUDDynamoDB" --role-arn "arn:aws:iam::012345678901:role/DummyRole" \ - --region us-east-1 \ - --definition file://../statemachine/local_testing.asl.json +aws stepfunctions create-state-machine --endpoint http://localhost:8083 \ + --name "local-lambda" \ + --definition "{ \ + \"StartAt\": \"Lambda Sum State\", \ + \"States\": { \ + \"Lambda Sum State\": { \ + \"Next\": \"Wait State\", \ + \"Type\": \"Task\", \ + \"InputPath\": \"$\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSumLambda\",\ + \"Payload.$\": \"$\" \ + } \ + }, \ + \"Wait State\": { \ + \"Type\": \"Wait\", \ + \"Seconds\": 3, \ + \"Next\": \"Lambda Square State\" \ + }, \ + \"Lambda Square State\": { \ + \"End\": true, \ + \"Type\": \"Task\", \ + \"InputPath\": \"$.Payload\", \ + \"OutputPath\": \"$.Payload\", \ + \"Resource\": \"arn:aws:states:::lambda:invoke\", \ + \"Parameters\": { \ + \"FunctionName\": \"arn:aws:lambda:us-east-1:123456789012:function:StepFunctionExampleSquareLambda\",\ + \"Payload.$\": \"$\" \ + } \ + } \ + }, \ + \"TimeoutSeconds\": 300 \ + }" --name "StepFunctionsLambdaStateMachine" --role-arn "arn:aws:iam::123456789012:role/DummyRole" ``` -#### Debug state machine - aws cli debugging - +#### Debug state machine - 3. Execute State Machine ```sh -# Get state machine ARN -aws stepfunctions list-state-machines --endpoint-url http://localhost:8083 +aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --input '{"x": 2, "y": 8, "z": 7}' \ + --state-machine [STATE-MACHINE-ARN] +``` -# Check state machine definition -aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] +#### Debug state machine - 4. Check State Machine Execution -# Start state machine manually -aws stepfunctions start-execution --endpoint-url http://localhost:8083 \ - --state-machine-arn [STATE-MACHINE-ARN] +Checking state machine definition +```sh +aws stepfunctions describe-state-machine --endpoint-url http://localhost:8083 \ + --state-machine-arn [STATE-MACHINE-ARN] +``` -# Check execution details -aws stepfunctions describe-execution --endpoint http://localhost:8083 \ +Checking state machine execution flow, execution and state variables +```sh +aws stepfunctions describe-execution --endpoint http://localhost:8083 \ --execution-arn [STATE-MACHINE-EXECUTION-ARN] +``` -# Check execution history -aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ - --execution-arn [STATE-MACHINE-EXECUTION-ARN] +Checking state machine states execution variables +```sh +aws stepfunctions get-execution-history --endpoint http://localhost:8083 \ + --execution-arn [STATE-MACHINE-EXECUTION-ARN] ``` --- +## Common Issues + +### SAM Local Connection Issues + +If tests are skipped with "Lambda invocation failed, SAM might not be running properly": +- Ensure SAM Local is running on port 3001 +- Check that you've started SAM with `--docker-network host` (on linux hosts) +- Verify the Lambda functions are correctly defined in template.yaml +- Check SAM logs for any errors + +### Step Functions Container Fails to Start + +If the Step Functions container fails to start: +- Check if the container image "amazon/aws-stepfunctions-local" is already in use with `docker ps -a` +- Verify Docker permissions and network settings +- Ensure port 8083 is available + +### Lambda Function Input/Output Issues + +If Lambda functions aren't receiving or returning expected data: +- Check that the input JSON files have the correct structure +- For the sum function, ensure it has "x", "y", and "z" fields +- For the square function, ensure it has a "result" field +- Verify the Lambda handler in app.py correctly processes the input + +### Step Functions Execution Failure + +If Step Functions executions fail: +- Check the execution history with the AWS CLI +- Verify the Lambda resource ARNs in the state machine definition +- Ensure the Step Functions container's LAMBDA_ENDPOINT points to the correct SAM Local port + +--- + ## Additional Resources - [Step Functions Local Guide](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html) +- [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) +- [AWS Lambda Local Testing](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-using-invoke.html) +- [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) - [Amazon States Language Documentation](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) -- [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html) - [PyTest Documentation](https://docs.pytest.org/) -- [Testcontainers Python Documentation](https://testcontainers-python.readthedocs.io/en/latest/) +- +[Top](#contents) + -[Top](#contents) \ No newline at end of file diff --git a/python-test-samples/step-functions-local-mock/README.md b/python-test-samples/step-functions-local-mock/README.md index 3c9b8c92..99f3fe73 100644 --- a/python-test-samples/step-functions-local-mock/README.md +++ b/python-test-samples/step-functions-local-mock/README.md @@ -105,6 +105,16 @@ Client: Docker Engine - Community ### Run the Unit Test - End to end python test + +#### Configure environment variables: + +``` shell +step-functions-local-mock$ +export AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' +export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' +export AWS_REGION='us-east-1' +``` + > Set up the python environment: ``` shell @@ -160,9 +170,9 @@ rm -rf venv/ > unsetting variables ```sh -unset AWS_ACCESS_KEY_ID='DUMMYIDEXAMPLE' -unset AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' -unset AWS_REGION='us-east-1' +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset AWS_REGION ``` > cleanning docker @@ -196,6 +206,8 @@ python3 -m pytest -s -v unit/src/test_step_functions_local.py::test_retry_path If you need to manually verify the state machine or execution details, you can use these commands: +Note: Unlike other samples in this repository, these commands should be run from the project root directory step-functions-local-lambda, not from within the /tests directory. These allows direct file paths without having to use "../" references + #### Configure environment variables: ```sh @@ -205,6 +217,7 @@ export AWS_SECRET_ACCESS_KEY='DUMMYEXAMPLEKEY' export AWS_REGION='us-east-1' ``` + #### Debug state machine - 1. Start the Step Functions Local container: ```sh

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 - AWS Lambda Hello World -

jAGYB{O<2L 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 - API Gateway - MOCK -

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 + AWS Lambda Hello World +