pulumi/tests/examples/examples_test.go
Justin Van Patten fe603568fd
Use provider.MainWithOptions to reduce boilerplate in integration tests (#7684)
We can use the new `provider.MainWithOptions` to reduce boilerplate in some of our testcomponent providers.

Also, while cleaning up here, I took this as an opportunity to replace use of `github.com/pkg/errors` in the `tests` dir to use the built-in functionality of the Go standard library.
2021-07-30 06:31:17 -07:00

366 lines
11 KiB
Go

// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package examples
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/blang/semver"
"github.com/stretchr/testify/assert"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
)
func TestAccMinimal(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "minimal"),
Config: map[string]string{
"name": "Pulumi",
},
Secrets: map[string]string{
"secret": "this is my secret message",
},
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
// Simple runtime validation that just ensures the checkpoint was written and read.
assert.NotNil(t, stackInfo.Deployment)
},
RunBuild: true,
})
integration.ProgramTest(t, &test)
}
func TestAccMinimal_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "minimal"),
Config: map[string]string{
"name": "Pulumi",
},
Secrets: map[string]string{
"secret": "this is my secret message",
},
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
// Simple runtime validation that just ensures the checkpoint was written and read.
assert.NotNil(t, stackInfo.Deployment)
},
RunBuild: true,
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderSimple(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/simple"),
Config: map[string]string{
"simple:config:w": "1",
"simple:config:x": "1",
"simple:config:y": "1",
},
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderSimple_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/simple"),
Config: map[string]string{
"simple:config:w": "1",
"simple:config:x": "1",
"simple:config:y": "1",
},
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderClassWithComments(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/class-with-comments"),
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderClassWithComments_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/class-with-comments"),
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderMultipleTurns(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/multiple-turns"),
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
for _, res := range stackInfo.Deployment.Resources {
if !providers.IsProviderType(res.Type) && res.Parent == "" {
assert.Equal(t, stackInfo.RootResource.URN, res.URN,
"every resource but the root resource should have a parent, but %v didn't", res.URN)
}
}
},
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderMultipleTurns_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/multiple-turns"),
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
for _, res := range stackInfo.Deployment.Resources {
if !providers.IsProviderType(res.Type) && res.Parent == "" {
assert.Equal(t, stackInfo.RootResource.URN, res.URN,
"every resource but the root resource should have a parent, but %v didn't", res.URN)
}
}
},
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderMultipleTurns2(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/multiple-turns-2"),
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderMultipleTurns2_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/multiple-turns-2"),
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderDerivedInputs(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/derived-inputs"),
})
integration.ProgramTest(t, &test)
}
func TestAccDynamicProviderDerivedInputs_withLocalState(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "dynamic-provider/derived-inputs"),
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccFormattable(t *testing.T) {
var formattableStdout, formattableStderr bytes.Buffer
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "formattable"),
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
// Note that we're abusing this hook to validate stdout. We don't actually care about the checkpoint.
stdout := formattableStdout.String()
assert.False(t, strings.Contains(stdout, "MISSING"))
},
Stdout: &formattableStdout,
Stderr: &formattableStderr,
})
integration.ProgramTest(t, &test)
}
func TestAccFormattable_withLocalState(t *testing.T) {
var formattableStdout, formattableStderr bytes.Buffer
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "formattable"),
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
// Note that we're abusing this hook to validate stdout. We don't actually care about the checkpoint.
stdout := formattableStdout.String()
assert.False(t, strings.Contains(stdout, "MISSING"))
},
Stdout: &formattableStdout,
Stderr: &formattableStderr,
CloudURL: "file://~",
})
integration.ProgramTest(t, &test)
}
func TestAccSecrets(t *testing.T) {
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "secrets"),
Config: map[string]string{
"message": "plaintext message",
},
Secrets: map[string]string{
"apiKey": "FAKE_API_KEY_FOR_TESTING",
},
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.NotNil(t, stackInfo.Deployment.SecretsProviders, "Deployment should have a secrets provider")
isEncrypted := func(v interface{}) bool {
if m, ok := v.(map[string]interface{}); ok {
sigKey := m[resource.SigKey]
if sigKey == nil {
return false
}
v, vOk := sigKey.(string)
if !vOk {
return false
}
if v != resource.SecretSig {
return false
}
ciphertext := m["ciphertext"]
if ciphertext == nil {
return false
}
_, cOk := ciphertext.(string)
return cOk
}
return false
}
assertEncryptedValue := func(m map[string]interface{}, key string) {
assert.Truef(t, isEncrypted(m[key]), "%s value should be encrypted", key)
}
assertPlaintextValue := func(m map[string]interface{}, key string) {
assert.Truef(t, !isEncrypted(m[key]), "%s value should not encrypted", key)
}
for _, res := range stackInfo.Deployment.Resources {
if res.Type == "pulumi-nodejs:dynamic:Resource" {
switch res.URN.Name() {
case "sValue", "sApply", "cValue", "cApply":
assertEncryptedValue(res.Inputs, "value")
assertEncryptedValue(res.Outputs, "value")
case "pValue", "pApply":
assertPlaintextValue(res.Inputs, "value")
assertPlaintextValue(res.Outputs, "value")
case "pDummy":
assertPlaintextValue(res.Outputs, "value")
case "sDummy":
// Creation of this resource passes in a custom resource options to ensure that "value" is
// treated as secret. In the state file, we'll see this as an uncrypted input with an
// encrypted output.
assertEncryptedValue(res.Outputs, "value")
case "rValue":
assertEncryptedValue(res.Inputs["value"].(map[string]interface{}), "secret")
assertEncryptedValue(res.Outputs["value"].(map[string]interface{}), "secret")
assertPlaintextValue(res.Inputs["value"].(map[string]interface{}), "plain")
assertPlaintextValue(res.Outputs["value"].(map[string]interface{}), "plain")
default:
contract.Assertf(false, "unknown name type: %s", res.URN.Name())
}
}
}
assertEncryptedValue(stackInfo.Outputs, "combinedApply")
assertEncryptedValue(stackInfo.Outputs, "combinedMessage")
assertPlaintextValue(stackInfo.Outputs, "plaintextApply")
assertPlaintextValue(stackInfo.Outputs, "plaintextMessage")
assertEncryptedValue(stackInfo.Outputs, "secretApply")
assertEncryptedValue(stackInfo.Outputs, "secretMessage")
assertEncryptedValue(stackInfo.Outputs, "richStructure")
},
})
integration.ProgramTest(t, &test)
}
func TestAccNodeCompatTests(t *testing.T) {
skipIfNotNode610(t)
test := getBaseOptions().
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "compat/v0.10.0/minimal"),
Config: map[string]string{
"name": "Pulumi",
},
Secrets: map[string]string{
"secret": "this is my secret message",
},
RunBuild: true,
})
integration.ProgramTest(t, &test)
}
func getCwd(t *testing.T) string {
cwd, err := os.Getwd()
if err != nil {
t.FailNow()
}
return cwd
}
func getBaseOptions() integration.ProgramTestOptions {
return integration.ProgramTestOptions{
Dependencies: []string{"@pulumi/pulumi"},
}
}
func getPythonBaseOptions() integration.ProgramTestOptions {
return integration.ProgramTestOptions{
Dependencies: []string{
filepath.Join("..", "sdk", "python", "env", "src"),
},
}
}
func skipIfNotNode610(t *testing.T) {
nodeVer, err := getNodeVersion()
if err != nil && nodeVer.Major == 6 && nodeVer.Minor == 10 {
t.Skip("Skipping 0.10.0 compat tests, because current node version is not 6.10.X")
}
}
func getNodeVersion() (semver.Version, error) {
var buf bytes.Buffer
nodeVersionCmd := exec.Command("node", "--version")
nodeVersionCmd.Stdout = &buf
if err := nodeVersionCmd.Run(); err != nil {
return semver.Version{}, fmt.Errorf("running node --version: %w", err)
}
return semver.ParseTolerant(buf.String())
}