mirror of https://github.com/pulumi/pulumi.git
184 lines
5.4 KiB
Go
184 lines
5.4 KiB
Go
// Copyright 2016-2024, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package tests
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"reflect"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/display"
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/stack"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func AssertStackResource(t TestingT, err error, changes display.ResourceChanges) (ok bool) {
|
|
t.Helper()
|
|
|
|
ok = true
|
|
ok = ok && assert.Nil(t, err, "expected no error, got %v", err)
|
|
ok = ok && assert.NotEmpty(t, changes, "expected at least 1 StepOp")
|
|
ok = ok && assert.NotZero(t, changes[deploy.OpCreate], "expected at least 1 Create")
|
|
return ok
|
|
}
|
|
|
|
func RequireStackResource(t TestingT, err error, changes display.ResourceChanges) {
|
|
t.Helper()
|
|
|
|
if !AssertStackResource(t, err, changes) {
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
func RequireSingleResource(t TestingT, resources []*resource.State, typ tokens.Type) *resource.State {
|
|
t.Helper()
|
|
|
|
var result *resource.State
|
|
for _, res := range resources {
|
|
if res.Type == typ {
|
|
require.Nil(t, result, "expected exactly 1 resource of type %q, got multiple", typ)
|
|
result = res
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, result, "expected exactly 1 resource of type %q, got none", typ)
|
|
return result
|
|
}
|
|
|
|
// RequireSingleNamedResource returns the single resource with the given name from the given list of resources. If more
|
|
// than one resource has the given name, the test fails. If no resources have the given name, the test fails.
|
|
func RequireSingleNamedResource(
|
|
t TestingT,
|
|
resources []*resource.State,
|
|
name string,
|
|
) *resource.State {
|
|
t.Helper()
|
|
|
|
var result *resource.State
|
|
for _, res := range resources {
|
|
if res.URN.Name() == name {
|
|
require.Nil(t, result, "expected exactly 1 resource named %q, got multiple", name)
|
|
result = res
|
|
}
|
|
}
|
|
|
|
require.NotNil(t, result, "expected exactly 1 resource named %q, got none", name)
|
|
return result
|
|
}
|
|
|
|
// AssertPropertyMapMember asserts that the given property map has a member with the given key and value.
|
|
func AssertPropertyMapMember(
|
|
t TestingT,
|
|
props resource.PropertyMap,
|
|
key string,
|
|
want resource.PropertyValue,
|
|
) (ok bool) {
|
|
t.Helper()
|
|
|
|
got, ok := props[resource.PropertyKey(key)]
|
|
if !assert.True(t, ok, "expected property %q", key) {
|
|
return false
|
|
}
|
|
|
|
return assert.Equal(t, want, got, "expected property %q to be %v", key, want)
|
|
}
|
|
|
|
// Like assert.Equal but also permits the actual value to be the JSON-serialized string form of the expected value.
|
|
func AssertEqualOrJSONEncoded(l *L, expect any, actual any, msg string) {
|
|
if actualS, ok := actual.(string); ok {
|
|
var a any
|
|
err := json.Unmarshal([]byte(actualS), &a)
|
|
if err == nil {
|
|
if reflect.DeepEqual(expect, a) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
assert.Equal(l, expect, actual, msg)
|
|
}
|
|
|
|
// Like assert.Equal but also permits secreted JSON-encoded values. The assert succeeds if either:
|
|
//
|
|
// actual == expect
|
|
// actual == secret(json(expect2))
|
|
//
|
|
// The second form expect2 usually has secrets stripped. If nil, the code assumes expect2=expect.
|
|
func AssertEqualOrJSONEncodedSecret(l *L, expect, expect2, actual any, msg string) {
|
|
if expect2 == nil {
|
|
expect2 = expect
|
|
}
|
|
if actualObj, ok := actual.(map[string]any); ok {
|
|
tagV, ok := actualObj["4dabf18193072939515e22adb298388d"]
|
|
if ok && tagV == "1b47061264138c4ac30d75fd1eb44270" {
|
|
actualS, ok := actualObj["value"].(string)
|
|
if ok {
|
|
var a any
|
|
err := json.Unmarshal([]byte(actualS), &a)
|
|
if err == nil && reflect.DeepEqual(expect2, a) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert.Equal(l, expect, actual, msg)
|
|
}
|
|
|
|
// Wraps a value in secret sentinel as seen on the RPC wire.
|
|
func Secret(x any) any {
|
|
return map[string]any{
|
|
"4dabf18193072939515e22adb298388d": "1b47061264138c4ac30d75fd1eb44270",
|
|
"value": x,
|
|
}
|
|
}
|
|
|
|
type AssertNoSecretLeaksOpts struct {
|
|
IgnoreResourceTypes []tokens.Type
|
|
Secrets []string
|
|
}
|
|
|
|
func (opts *AssertNoSecretLeaksOpts) isIgnored(ty tokens.Type) bool {
|
|
for _, t := range opts.IgnoreResourceTypes {
|
|
if ty == t {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func AssertNoSecretLeaks(l require.TestingT, snap *deploy.Snapshot, opts AssertNoSecretLeaksOpts) {
|
|
// Remove states for resources with types in opts.IgnoreResourceTypes from the snap to exclude these states from
|
|
// the secret leak checks.
|
|
var filteredResourceStates []*resource.State
|
|
for _, r := range snap.Resources {
|
|
if !opts.isIgnored(r.Type) {
|
|
filteredResourceStates = append(filteredResourceStates, r)
|
|
}
|
|
}
|
|
snap.Resources = filteredResourceStates
|
|
|
|
// Ensure that secrets do not leak to the state.
|
|
deployment, err := stack.SerializeDeployment(context.Background(), snap, false /*showSecrets*/)
|
|
require.NoError(l, err)
|
|
bytes, err := json.MarshalIndent(deployment, "", " ")
|
|
require.NoError(l, err)
|
|
for _, s := range opts.Secrets {
|
|
require.NotContainsf(l, string(bytes), s, "Detected a secret leak in state: %s", s)
|
|
}
|
|
}
|