diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 74d24b6..c0d4c0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,21 +61,9 @@ jobs: make bootstrap make deploy - - name: Smoke Test + - name: Integration Tests run: | - awslocal --version - awslocal lambda invoke --cli-binary-format raw-in-base64-out --function-name my-lambda-rds-query-helper --payload '{"sqlQuery": "show tables", "secretName":"/rdsinitexample/rds/creds/mysql-01"}' output - echo "show tables:" - cat output - awslocal lambda invoke --cli-binary-format raw-in-base64-out --function-name my-lambda-rds-query-helper --payload '{"sqlQuery": "select Author from Books", "secretName":"/rdsinitexample/rds/creds/mysql-01"}' output - echo "select Author from Books:" - cat output - return_status=$(cat output | jq -r .status) - if [ "SUCCESS" != ${return_status} ]; then - echo "unexpected response: ${return_status}" - cat output - exit 1 - fi + npm test - name: Show Logs if: always() diff --git a/jest.config.js b/jest.config.js index 20b0bed..c5d2def 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,6 @@ module.exports = { - roots: ['/test'], + testEnvironment: 'node', + roots: ['/tests'], testMatch: ['**/*.test.ts'], transform: { '^.+\\.tsx?$': 'ts-jest' diff --git a/package.json b/package.json index ea76ec2..27a71c6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,11 @@ "jest": "^27.2.4", "ts-jest": "^27.0.5", "ts-node": "^10.2.1", - "typescript": "^4.4.3" + "typescript": "^4.4.3", + "@aws-sdk/client-lambda": "^3.0.0", + "@aws-sdk/client-rds": "^3.0.0", + "@aws-sdk/client-secrets-manager": "^3.0.0", + "@aws-sdk/client-s3": "^3.0.0" }, "dependencies": { "aws-cdk-lib": "^2.0.0", diff --git a/tests/cdk-rds.test.ts b/tests/cdk-rds.test.ts new file mode 100644 index 0000000..5a31de2 --- /dev/null +++ b/tests/cdk-rds.test.ts @@ -0,0 +1,122 @@ +import { Template } from 'aws-cdk-lib/assertions'; +import * as cdk from 'aws-cdk-lib'; +import { + LambdaClient, + InvokeCommand, + GetFunctionCommand +} from '@aws-sdk/client-lambda'; +import { + RDSClient, + DescribeDBInstancesCommand, + DBInstance +} from '@aws-sdk/client-rds'; +import { + SecretsManagerClient, + GetSecretValueCommand +} from '@aws-sdk/client-secrets-manager'; +import { + S3Client, + ListBucketsCommand +} from '@aws-sdk/client-s3'; + +const config = { + endpoint: 'http://localhost:4566', + region: 'us-east-1', + credentials: { + accessKeyId: 'test', + secretAccessKey: 'test' + } +}; + +const lambdaClient = new LambdaClient(config); +const rdsClient = new RDSClient(config); +const secretsClient = new SecretsManagerClient(config); +const s3Client = new S3Client(config); + +describe('CDK RDS Stack', () => { + test('Lambda function exists', async () => { + try { + const command = new GetFunctionCommand({ + FunctionName: 'my-lambda-rds-query-helper' + }); + const response = await lambdaClient.send(command); + expect(response.Configuration?.FunctionName).toBe('my-lambda-rds-query-helper'); + } catch (error) { + expect(error).toBeFalsy(); + } + }); + + test('RDS instance exists', async () => { + try { + const command = new DescribeDBInstancesCommand({}); + const response = await rdsClient.send(command); + expect(response.DBInstances?.length).toBeGreaterThan(0); + const instance = response.DBInstances?.find((db: DBInstance) => db.Engine === 'mysql'); + expect(instance).toBeDefined(); + } catch (error) { + expect(error).toBeFalsy(); + } + }); + + test('Secrets Manager secret exists', async () => { + try { + const command = new GetSecretValueCommand({ + SecretId: '/rdsinitexample/rds/creds/mysql-01' + }); + const response = await secretsClient.send(command); + expect(response.SecretString).toBeDefined(); + } catch (error) { + expect(error).toBeFalsy(); + } + }); + + test('Lambda function returns correct response for show tables', async () => { + const payload = { + sqlQuery: 'show tables', + secretName: '/rdsinitexample/rds/creds/mysql-01' + }; + + try { + const command = new InvokeCommand({ + FunctionName: 'my-lambda-rds-query-helper', + Payload: Buffer.from(JSON.stringify(payload)) + }); + + const response = await lambdaClient.send(command); + const result = JSON.parse(Buffer.from(response.Payload!).toString()); + expect(result.status).toBe('SUCCESS'); + expect(Array.isArray(result.results)).toBeTruthy(); + // Verify the expected tables are present + const tables = result.results.map((r: any) => r.Tables_in_main); + expect(tables).toContain('Books'); + expect(tables).toContain('OrderDetails'); + } catch (error) { + expect(error).toBeFalsy(); + } + }); + + test('Lambda function returns correct response for select query', async () => { + const payload = { + sqlQuery: 'select Author from Books', + secretName: '/rdsinitexample/rds/creds/mysql-01' + }; + + try { + const command = new InvokeCommand({ + FunctionName: 'my-lambda-rds-query-helper', + Payload: Buffer.from(JSON.stringify(payload)) + }); + + const response = await lambdaClient.send(command); + const result = JSON.parse(Buffer.from(response.Payload!).toString()); + expect(result.status).toBe('SUCCESS'); + expect(Array.isArray(result.results)).toBeTruthy(); + // Verify we have author data + const authors = result.results.map((r: any) => r.Author); + expect(authors).toContain('Jane Doe'); + expect(authors).toContain('LocalStack'); + } catch (error) { + expect(error).toBeFalsy(); + } + }); +});