From b26de2f461725d3b780d9340090510b9be156822 Mon Sep 17 00:00:00 2001 From: Harsh Mishra Date: Tue, 13 Oct 2026 07:44:29 +0530 Subject: [PATCH] Add CI that checks the demo handles an injected DynamoDB fault Add CI that checks the demo handles an injected DynamoDB fault Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM --- .github/workflows/main.yml | 51 +++++++++++++++++++++++++++++++++++++ Makefile | 7 +++-- README.md | 2 ++ test.sh | 52 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100755 test.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..64f3676 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,51 @@ +name: Run on LocalStack + +on: + push: + paths-ignore: + - 'README.md' + branches: + - master + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + chaos: + name: Run the chaos demo + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Install lstk + run: | + npm install -g @localstack/lstk + lstk setup aws --force --non-interactive + + - name: Build the demo + run: dotnet build ChaosEngDemo + + - name: Start LocalStack + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + run: lstk start + + - name: Run tests + run: make test + + - name: Show LocalStack logs + if: failure() + run: lstk logs | tail -200 diff --git a/Makefile b/Makefile index 38c1053..c86ae4e 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ logs: ## Save the logs in a separate file @lstk logs > logs.txt run: ## Run the chaos demo - dotnet run + dotnet run --project ChaosEngDemo -.PHONY: usage install start stop logs run +test: ## Run the demo with and without a DynamoDB fault injected + ./test.sh + +.PHONY: usage install start stop logs run test diff --git a/README.md b/README.md index 7a11cdb..6e64346 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ make start * Retrieve the item and print its attributes to the console. * Handle any exceptions that may occur during these operations. +3. **Test the error handling:** `make test` runs the demo twice. The first run has a fault injected on DynamoDB `PutItem` through the [LocalStack Chaos API](https://docs.localstack.cloud/aws/capabilities/chaos-engineering/chaos-api/), so the program reports the `AccessDeniedException`. The second run has no fault and reads the item back. + ## Key Concepts * **LocalStack:** Provides a local AWS cloud stack for development and testing. This allows you to run the demo without needing a real AWS account. diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..815b7ef --- /dev/null +++ b/test.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Run the demo twice: +# 1. with a fault injected on DynamoDB PutItem through the LocalStack Chaos API, +# and check the program reports the AWS SDK exception +# 2. without the fault, and check the item is stored and read back +set -euo pipefail + +ENDPOINT=${ENDPOINT:-http://localhost:4566} +RUN_DEMO=${RUN_DEMO:-dotnet run --project ChaosEngDemo} +# A non-retryable error, so the SDK fails fast instead of backing off +FAULT='[{"service": "dynamodb", "region": "us-east-1", "operation": "PutItem", "probability": 1.0, + "error": {"statusCode": 400, "code": "AccessDeniedException"}}]' + +remove_fault() { + curl -s -X DELETE "$ENDPOINT/_localstack/chaos/faults" -H 'Content-Type: application/json' -d "$FAULT" > /dev/null +} +trap remove_fault EXIT + +echo "Injecting a fault on DynamoDB PutItem" +curl -s --fail-with-body -X POST "$ENDPOINT/_localstack/chaos/faults" \ + -H 'Content-Type: application/json' -d "$FAULT" > /dev/null + +output=$($RUN_DEMO 2>&1) +echo "$output" +if [[ "$output" != *"AWS SDK Exception caught!"* || "$output" != *"Error Code: AccessDeniedException"* ]]; then + echo "Test failed: the demo did not report the injected fault" + exit 1 +fi +if [[ "$output" == *"Item inserted."* ]]; then + echo "Test failed: the item was inserted despite the fault" + exit 1 +fi +echo "PASS: the demo caught the injected AccessDeniedException" + +remove_fault +echo "Fault removed, running the demo again" + +output=$($RUN_DEMO 2>&1) +echo "$output" +if [[ "$output" != *"Item retrieved:"* || "$output" != *"Name: Chaos Widget"* ]]; then + echo "Test failed: the demo did not read the item back" + exit 1 +fi +name=$(lstk aws dynamodb get-item --table-name Products --key '{"Id": {"S": "123"}}' \ + --query Item.Name.S --output text) +if [ "$name" != "Chaos Widget" ]; then + echo "Test failed: item 123 in the Products table has name '$name'" + exit 1 +fi +echo "PASS: the item was stored in DynamoDB and read back" + +echo "Test passed: the demo handles DynamoDB faults and recovers once they are gone"