Skip to content

Commit b95e810

Browse files
Examples of testing with Pulumi (#608)
* .NET unit test * Add an example of PaC-based testing * Add a TypeScript unit test * Add a python test * Add an integration test
1 parent d617a14 commit b95e810

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2112
-19
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ See the [Pulumi documentation](https://www.pulumi.com/docs/) for more details on
4141
- [Twilio](#twilio)
4242
- [Linode](#linode)
4343
- [Packet](#packet)
44+
- [Testing](#testing)
4445

4546
## AWS
4647

@@ -358,3 +359,13 @@ Example | Description |
358359
----- | --------- |
359360
[Web Server](packet-py-webserver) | Build a web server on Packet.net.
360361

362+
## Testing
363+
364+
Example | Description |
365+
----- | --------- |
366+
[Unit Tests in TypeScript](testing-unit-ts) | Mock-based unit tests in TypeScript.
367+
[Unit Tests in Python](testing-unit-python) | Mock-based unit tests in Python.
368+
[Unit Tests in Go](testing-unit-go) | Mock-based unit tests in Go.
369+
[Unit Tests in C#](testing-unit-cs) | Mock-based unit tests in C#.
370+
[Testing with Policies](testing-pac-ts) | Tests based on Policy-as-Code in TypeScript.
371+
[Integration Testing in Go](testing-integration) | Deploy-check-destroy tests in Go.

aws-cs-fargate/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ infrastructure. [`./Infra/Program.cs`](./Infra/Program.cs) defines the project's
2020
## Prerequisites
2121

2222
* [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
23-
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI
24-
is configured, this will just work)
23+
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is configured, no further changes are required)
2524
* [Install .NET Core 3](https://dotnet.microsoft.com/download)
2625
* [Install Docker](https://docs.docker.com/install/)
2726

aws-go-fargate/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ This example is inspired by [Docker's Getting Started Tutorial](https://docs.doc
1010
## Prerequisites
1111

1212
* [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
13-
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI
14-
is configured, this will just work)
13+
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is configured, no further changes are required)
1514
* [Install Go](https://golang.org/doc/install)
1615

1716
## Running the Example

aws-py-fargate/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ This example is inspired by [Docker's Getting Started Tutorial](https://docs.doc
1010
## Prerequisites
1111

1212
* [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
13-
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI
14-
is configured, this will just work)
13+
* [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is configured, no further changes are required)
1514

1615
## Running the Example
1716

aws-ts-hello-fargate/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ databases, and so on.
1818

1919
- [Node.js](https://nodejs.org/en/download/)
2020
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
21-
- [Connect Pulumi with your AWS account](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is
22-
configured, this will just work)
21+
- [Connect Pulumi with your AWS account](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is configured, no further changes are required)
2322

2423
## Running the Example
2524

azure-go-webserver-component/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ defining a `WebServer` class, we can hide many details (see [here](./webserver.t
1111

1212
- [Node.js](https://nodejs.org/en/download/)
1313
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
14-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is
15-
configured, this will just work)
14+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1615

1716
## Running the App
1817

azure-py-vm-scaleset/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ This example provisions a Scale Set of Linux web servers with nginx deployed, co
88

99
- [Node.js](https://nodejs.org/en/download/)
1010
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
11-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is
12-
configured, this will just work)
11+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1312

1413
## Running the App
1514

azure-ts-cosmosdb-logicapp/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ At the time of writing, there is no native Pulumi resource for defining an API C
88

99
- [Node.js](https://nodejs.org/en/download/)
1010
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
11-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, this will just work)
11+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1212

1313
## Running the App
1414

azure-ts-vm-scaleset/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ This example provisions a Scale Set of Linux web servers with nginx deployed, co
88

99
- [Node.js](https://nodejs.org/en/download/)
1010
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
11-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is
12-
configured, this will just work)
11+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1312

1413
## Running the App
1514

azure-ts-webserver-component/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ defining a `WebServer` class, we can hide many details (see [here](./webserver.t
1111

1212
- [Node.js](https://nodejs.org/en/download/)
1313
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
14-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is
15-
configured, this will just work)
14+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1615

1716
## Running the App
1817

azure-ts-webserver/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ This example provisions a Linux web server in an Azure Virtual Machine and gives
88

99
- [Node.js](https://nodejs.org/en/download/)
1010
- [Download and install the Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
11-
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is
12-
configured, this will just work)
11+
- [Connect Pulumi with your Azure account](https://www.pulumi.com/docs/intro/cloud-providers/azure/setup/) (if your `az` CLI is configured, no further changes are required)
1312

1413
## Running the App
1514

testing-integration/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.pulumi/
2+
**/bin/
3+
node_modules/
4+

testing-integration/README.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Integration Testing of Pulumi programs in Go
2+
3+
This integration test treats infrastucture deployed by a Pulumi program as a "black box". If deploys the infrastructure, retrieves an endpoint from stack outputs, sends an HTTP request to the endpoint, validates the response, and tears down the infrastructure again.
4+
5+
This test deploys a static website as an AWS S3 bucket and checks that the site is reachable.
6+
7+
## Prerequisites
8+
9+
1. [Install Go](https://golang.org/doc/install).
10+
2. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/).
11+
3. [Configure Pulumi to Use AWS](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/) (if your AWS CLI is configured, no further changes are required).
12+
13+
## Running the tests
14+
15+
Run the tests:
16+
17+
```
18+
$ go test
19+
20+
...
21+
22+
PASS
23+
ok github.com/pulumi/examples/testing-integration 65.749s
24+
```

testing-integration/examples_test.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright 2016-2020, Pulumi Corporation. All rights reserved.
2+
3+
package examples
4+
5+
import (
6+
"fmt"
7+
"io/ioutil"
8+
"net/http"
9+
"os"
10+
"path"
11+
"strings"
12+
"testing"
13+
"time"
14+
15+
"github.com/pulumi/pulumi/pkg/testing/integration"
16+
"github.com/stretchr/testify/assert"
17+
)
18+
19+
func TestS3Website(t *testing.T) {
20+
cwd, err := os.Getwd()
21+
if err != nil {
22+
t.FailNow()
23+
}
24+
25+
test := integration.ProgramTestOptions{
26+
Dir: path.Join(cwd, "program"),
27+
Quick: true,
28+
SkipRefresh: true,
29+
Config: map[string]string{
30+
"aws:region": "us-west-1",
31+
},
32+
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
33+
assertHTTPResult(t, "http://"+stack.Outputs["websiteUrl"].(string), nil, func(body string) bool {
34+
return assert.Contains(t, body, "Hello, Pulumi!")
35+
})
36+
},
37+
}
38+
integration.ProgramTest(t, &test)
39+
}
40+
41+
func assertHTTPResult(t *testing.T, output interface{}, headers map[string]string, check func(string) bool) bool {
42+
return assertHTTPResultWithRetry(t, output, headers, 5*time.Minute, check)
43+
}
44+
45+
func assertHTTPResultWithRetry(t *testing.T, output interface{}, headers map[string]string, maxWait time.Duration, check func(string) bool) bool {
46+
return assertHTTPResultShapeWithRetry(t, output, headers, maxWait, func(string) bool { return true }, check)
47+
}
48+
49+
func assertHTTPResultShapeWithRetry(t *testing.T, output interface{}, headers map[string]string, maxWait time.Duration,
50+
ready func(string) bool, check func(string) bool) bool {
51+
hostname, ok := output.(string)
52+
if !assert.True(t, ok, fmt.Sprintf("expected `%s` output", output)) {
53+
return false
54+
}
55+
56+
if !(strings.HasPrefix(hostname, "http://") || strings.HasPrefix(hostname, "https://")) {
57+
hostname = fmt.Sprintf("http://%s", hostname)
58+
}
59+
60+
startTime := time.Now()
61+
count, sleep := 0, 0
62+
for {
63+
now := time.Now()
64+
req, err := http.NewRequest("GET", hostname, nil)
65+
if !assert.NoError(t, err) {
66+
return false
67+
}
68+
69+
for k, v := range headers {
70+
// Host header cannot be set via req.Header.Set(), and must be set
71+
// directly.
72+
if strings.ToLower(k) == "host" {
73+
req.Host = v
74+
continue
75+
}
76+
req.Header.Set(k, v)
77+
}
78+
79+
client := &http.Client{Timeout: time.Second * 10}
80+
resp, err := client.Do(req)
81+
if err == nil && resp.StatusCode == 200 {
82+
if !assert.NotNil(t, resp.Body, "resp.body was nil") {
83+
return false
84+
}
85+
86+
// Read the body
87+
defer resp.Body.Close()
88+
body, err := ioutil.ReadAll(resp.Body)
89+
if !assert.NoError(t, err) {
90+
return false
91+
}
92+
93+
bodyText := string(body)
94+
95+
// Even if we got 200 and a response, it may not be ready for assertion yet - that's specific per test.
96+
if ready(bodyText) {
97+
// Verify it matches expectations
98+
return check(bodyText)
99+
}
100+
}
101+
if now.Sub(startTime) >= maxWait {
102+
fmt.Printf("Timeout after %v. Unable to http.get %v successfully.", maxWait, hostname)
103+
return false
104+
}
105+
count++
106+
// delay 10s, 20s, then 30s and stay at 30s
107+
if sleep > 30 {
108+
sleep = 30
109+
} else {
110+
sleep += 10
111+
}
112+
time.Sleep(time.Duration(sleep) * time.Second)
113+
fmt.Printf("Http Error: %v\n", err)
114+
fmt.Printf(" Retry: %v, elapsed wait: %v, max wait %v\n", count, now.Sub(startTime), maxWait)
115+
}
116+
117+
return false
118+
}

testing-integration/go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/pulumi/examples/testing-integration
2+
3+
go 1.13
4+
5+
require (
6+
github.com/pkg/errors v0.8.1
7+
github.com/pulumi/pulumi v1.13.0
8+
github.com/stretchr/testify v1.4.1-0.20191106224347-f1bd0923b832
9+
)
10+
11+
replace github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.4.3+incompatible

0 commit comments

Comments
 (0)