forked from pulumi/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an example of unit testing with Jest (pulumi#1233)
* Add an example of unit testing with Jest * Update testing-unit-ts-mocks-jest/README.md * Un-export the untested Co-authored-by: Laura Santamaria <[email protected]>
- Loading branch information
1 parent
df13f6d
commit 69c9a72
Showing
9 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/bin/ | ||
/node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
name: testing-unit-ts-mocks-jest | ||
runtime: nodejs | ||
description: An example of using Jest with Pulumi to write test-driven code that mocks AWS infrastructure. | ||
template: | ||
config: | ||
aws:region: | ||
description: The AWS region to deploy into | ||
default: us-east-2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Unit Testing AWS Infrastructure with Jest | ||
|
||
An example of using [Pulumi](https://pulumi.com/) with [Jest](https://jestjs.io/), the JavaScript testing framework, to write in-memory unit tests that mock AWS infrastructure. The program under test deploys a single [AWS Lambda function](https://aws.amazon.com/lambda/) and an associated [Lambda Function URL](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html). | ||
|
||
[![Deploy with Pulumi](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/tree/master/testing-unit-ts-mocks-jest) | ||
|
||
## Prerequisites | ||
|
||
1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/). | ||
1. [Install Node.js](https://www.pulumi.com/docs/intro/languages/javascript/). | ||
1. Configure your [AWS credentials](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/). | ||
|
||
### Deploying the App | ||
|
||
1. Clone this repo, change to this directory, then create a new [stack](https://www.pulumi.com/docs/intro/concepts/stack/) for the project: | ||
|
||
```bash | ||
pulumi stack init | ||
``` | ||
|
||
1. Specify an AWS region to deploy into: | ||
|
||
```bash | ||
pulumi config set aws:region us-west-2 | ||
``` | ||
|
||
1. Install Node dependencies and run the tests: | ||
|
||
```bash | ||
npm install | ||
npm test | ||
``` | ||
|
||
In a few moments, the tests should pass. | ||
|
||
1. If you'd like to deploy the program as well, run `pulumi up`. In a few moments, the `FunctionUrl` of the `timeURL` Lambda will be emitted as a Pulumi [stack output](https://www.pulumi.com/docs/intro/concepts/stack/#outputs) called `audioURL`: | ||
```bash | ||
... | ||
Outputs: | ||
audioURL: "https://o3vbc73qd2vxrhtaao5v53yeaa0sricr.lambda-url.us-west-2.on.aws/" | ||
``` | ||
1. When you're ready, destroy your stack and remove it: | ||
|
||
```bash | ||
pulumi destroy --yes | ||
pulumi stack rm --yes | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2016-2022, Pulumi Corporation. All rights reserved. | ||
|
||
import * as pulumi from "@pulumi/pulumi"; | ||
import "jest"; | ||
|
||
function promiseOf<T>(output: pulumi.Output<T>): Promise<T> { | ||
return new Promise(resolve => output.apply(resolve)); | ||
} | ||
|
||
describe("My speaking clock", () => { | ||
|
||
// Define the infra variable as a type whose shape matches the that of the | ||
// to-be-defined resources module. | ||
// https://www.typescriptlang.org/docs/handbook/2/typeof-types.html | ||
let infra: typeof import("./resources"); | ||
|
||
beforeAll(() => { | ||
|
||
// Put Pulumi in unit-test mode, mocking all calls to cloud-provider APIs. | ||
pulumi.runtime.setMocks({ | ||
|
||
// Mock calls to create new resources and return a canned response. | ||
newResource: (args: pulumi.runtime.MockResourceArgs): {id: string, state: any} => { | ||
|
||
// Here, we're returning a same-shaped object for all resource types. | ||
// We could, however, use the arguments passed into this function to | ||
// customize the mocked-out properties of a particular resource. | ||
// See the unit-testing docs for details: | ||
// https://www.pulumi.com/docs/guides/testing/unit | ||
return { | ||
id: `${args.name}-id`, | ||
state: args.inputs, | ||
}; | ||
}, | ||
|
||
// Mock function calls and return whatever input properties were provided. | ||
call: (args: pulumi.runtime.MockCallArgs) => { | ||
return args.inputs; | ||
}, | ||
}); | ||
}); | ||
|
||
beforeEach(async () => { | ||
|
||
// Dynamically import the resources module. | ||
infra = await import("./resources"); | ||
}); | ||
|
||
describe("function URL", () => { | ||
|
||
it("is publicly accessible", async () => { | ||
const authType = await promiseOf(infra.timeURL.authorizationType); | ||
expect(authType).toBe("NONE"); | ||
}); | ||
|
||
it("is CORS-friendly", async () => { | ||
const authType = await promiseOf(infra.timeURL.cors); | ||
expect(authType).toEqual({ | ||
allowOrigins: ["*"], | ||
allowMethods: ["GET"], | ||
}); | ||
}); | ||
|
||
it("is bound to the right Lambda function", async () => { | ||
const timeFuncName = await promiseOf(infra.timeFunction.name); | ||
const timeURLFunc = await promiseOf(infra.timeURL.functionName); | ||
expect(timeFuncName).toEqual(timeURLFunc); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// Copyright 2016-2022, Pulumi Corporation. All rights reserved. | ||
|
||
import "./resources"; | ||
|
||
export { audioURL } from "./resources"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Copyright 2016-2022, Pulumi Corporation. All rights reserved. | ||
|
||
import type { Config } from "@jest/types"; | ||
|
||
const config: Config.InitialOptions = { | ||
preset: "ts-jest", | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"name": "testing-unit-ts-mocks-jest", | ||
"devDependencies": { | ||
"@types/jest": "^28.1.1", | ||
"@types/node": "^14", | ||
"ts-jest": "^28.0.5" | ||
}, | ||
"dependencies": { | ||
"@pulumi/aws": "^5.0.0", | ||
"@pulumi/awsx": "^0.40.0", | ||
"@pulumi/pulumi": "^3.0.0", | ||
"node-fetch": "^2.6.7" | ||
}, | ||
"scripts": { | ||
"test": "jest" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2016-2022, Pulumi Corporation. All rights reserved. | ||
|
||
import * as pulumi from "@pulumi/pulumi"; | ||
import * as aws from "@pulumi/aws"; | ||
|
||
const fetch = require("node-fetch"); | ||
|
||
// Convert the current time into a speech-friendly string. | ||
function getSpeechText() { | ||
const now = new Date(); | ||
now.setSeconds(now.getSeconds() + 6); | ||
|
||
const local = new Date(now.toLocaleString("en-US", { timeZone: "America/Los_Angeles" })); | ||
const localHour = local.getHours(); | ||
const localMinute = local.getMinutes(); | ||
const h = localHour > 12 ? localHour - 12 : localHour; | ||
const m = localMinute < 10 ? `oh ${localMinute}` : localMinute; // So 2:03 -> "two-oh-three" | ||
const s = local.getSeconds(); | ||
|
||
return `At the tone, the time will be ${h} ${m}. And ${s} seconds.`; | ||
} | ||
|
||
export const timeFunction = new aws.lambda.CallbackFunction("time-function", { | ||
|
||
// Update the Lambda callback body to convert the current time into an MP3 file. | ||
callback: async () => { | ||
const text = getSpeechText(); | ||
const speechURL = `https://translate.google.com/translate_tts?ie=UTF-8&q=${encodeURIComponent(text)}&textLen=${text.length}&tl=en&client=tw-ob`; | ||
const beepURL = "https://www.pulumi.com/uploads/beep.mp3"; | ||
const ttsResponse = await fetch(speechURL); | ||
const beepResponse = await fetch(beepURL); | ||
const speech = await ttsResponse.arrayBuffer(); | ||
const beep = await beepResponse.arrayBuffer(); | ||
|
||
// Tack a beep onto the end of the audio returned from Google Translate, then | ||
// render the whole thing as a base-64 encoded string. | ||
const body = Buffer.concat([Buffer.from(speech), Buffer.from(beep)]).toString("base64"); | ||
|
||
// Return an appropriately shaped HTTP response. | ||
return { | ||
body, | ||
headers: { "Content-Type": "audio/mpeg" }, | ||
isBase64Encoded: true, | ||
statusCode: 200, | ||
}; | ||
}, | ||
}); | ||
|
||
export const timeURL = new aws.lambda.FunctionUrl("time-url", { | ||
functionName: timeFunction.name, | ||
authorizationType: "NONE", | ||
cors: { | ||
allowOrigins: ["*"], | ||
allowMethods: ["GET"], | ||
}, | ||
}); | ||
|
||
// Export the public URL of our shiny new service. | ||
export const audioURL = timeURL.functionUrl; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"compilerOptions": { | ||
"strict": true, | ||
"outDir": "bin", | ||
"target": "es2016", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"sourceMap": true, | ||
"experimentalDecorators": true, | ||
"pretty": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitReturns": true, | ||
"forceConsistentCasingInFileNames": true | ||
}, | ||
"files": [ | ||
"index.ts" | ||
] | ||
} |