Add tests for serializing PropertyMaps (#3533)
* WIP - Add tests for serializing PropertyMaps * Apply suggestions from code review Co-Authored-By: Pat Gavlin <pat@pulumi.com> * Cleanup tests
This commit is contained in:
parent
bf16f3a541
commit
4e59263a9c
|
@ -88,16 +88,23 @@ func (t *trackingDecrypter) SecureValues() []string {
|
|||
return t.secureValues
|
||||
}
|
||||
|
||||
// NewBlindingDecrypter returns a Decrypter that instead of decrypting data, just returns "[secret]", it can
|
||||
// BlindingCrypter returns a Crypter that instead of decrypting or encrypting data, just returns "[secret]", it can
|
||||
// be used when you want to display configuration information to a user but don't want to prompt for a password
|
||||
// so secrets will not be decrypted.
|
||||
// so secrets will not be decrypted or encrypted.
|
||||
var BlindingCrypter Crypter = blindingCrypter{}
|
||||
|
||||
// NewBlindingDecrypter returns a blinding decrypter.
|
||||
func NewBlindingDecrypter() Decrypter {
|
||||
return blindingDecrypter{}
|
||||
return blindingCrypter{}
|
||||
}
|
||||
|
||||
type blindingDecrypter struct{}
|
||||
type blindingCrypter struct{}
|
||||
|
||||
func (b blindingDecrypter) DecryptValue(ciphertext string) (string, error) {
|
||||
func (b blindingCrypter) DecryptValue(ciphertext string) (string, error) {
|
||||
return "[secret]", nil
|
||||
}
|
||||
|
||||
func (b blindingCrypter) EncryptValue(plaintext string) (string, error) {
|
||||
return "[secret]", nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -191,3 +193,153 @@ func TestUnknownSig(t *testing.T) {
|
|||
_, err := DeserializePropertyValue(rawProp, config.NewPanicCrypter())
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCustomSerialization(t *testing.T) {
|
||||
textAsset, err := resource.NewTextAsset("alpha beta gamma")
|
||||
assert.NoError(t, err)
|
||||
|
||||
strProp := resource.NewStringProperty("strProp")
|
||||
|
||||
computed := resource.Computed{Element: strProp}
|
||||
output := resource.Output{Element: strProp}
|
||||
secret := &resource.Secret{Element: strProp}
|
||||
|
||||
propMap := resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
// Primitive types
|
||||
"nil": nil,
|
||||
"bool": true,
|
||||
"int32": int64(41),
|
||||
"int64": int64(42),
|
||||
"float32": float32(2.5),
|
||||
"float64": float64(1.5),
|
||||
"string": "string literal",
|
||||
|
||||
// Data structures
|
||||
"array": []interface{}{"a", true, float64(32)},
|
||||
"array-empty": []interface{}{},
|
||||
|
||||
"map": map[string]interface{}{
|
||||
"a": true,
|
||||
"b": float64(88),
|
||||
"c": "c-see-saw",
|
||||
"d": "d-dee-daw",
|
||||
},
|
||||
"map-empty": map[string]interface{}{},
|
||||
|
||||
// Specialized resource types
|
||||
"asset-text": textAsset,
|
||||
|
||||
"computed": computed,
|
||||
"output": output,
|
||||
"secret": secret,
|
||||
})
|
||||
|
||||
assert.True(t, propMap.ContainsSecrets())
|
||||
assert.True(t, propMap.ContainsUnknowns())
|
||||
|
||||
// Confirm the expected shape of serializing a ResourceProperty and PropertyMap using the
|
||||
// reflection-based default JSON encoder. This should NOT be used when serializing resources,
|
||||
// but we confirm the expected shape here while we migrate older code that relied on the
|
||||
// specific format.
|
||||
t.Run("SerializeToJSON", func(t *testing.T) {
|
||||
b, err := json.Marshal(propMap)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshalling PropertyMap: %v", err)
|
||||
}
|
||||
json := string(b)
|
||||
|
||||
// Look for the specific JSON serialization of the properties.
|
||||
tests := []string{
|
||||
// Primitives
|
||||
`"nil":{"V":null}`,
|
||||
`"bool":{"V":true}`,
|
||||
`"string":{"V":"string literal"}}`,
|
||||
`"float32":{"V":2.5}`,
|
||||
`"float64":{"V":1.5}`,
|
||||
`"int32":{"V":41}`,
|
||||
`"int64":{"V":42}`,
|
||||
|
||||
// Data structures
|
||||
`array":{"V":[{"V":"a"},{"V":true},{"V":32}]}`,
|
||||
`"array-empty":{"V":[]}`,
|
||||
`"map":{"V":{"a":{"V":true},"b":{"V":88},"c":{"V":"c-see-saw"},"d":{"V":"d-dee-daw"}}}`,
|
||||
`"map-empty":{"V":{}}`,
|
||||
|
||||
// Specialized resource types
|
||||
// nolint: lll
|
||||
`"asset-text":{"V":{"4dabf18193072939515e22adb298388d":"c44067f5952c0a294b673a41bacd8c17","hash":"64989ccbf3efa9c84e2afe7cee9bc5828bf0fcb91e44f8c1e591638a2c2e90e3","text":"alpha beta gamma"}}`,
|
||||
|
||||
`"computed":{"V":{"Element":{"V":"strProp"}}}`,
|
||||
`"output":{"V":{"Element":{"V":"strProp"}}}`,
|
||||
`"secret":{"V":{"Element":{"V":"strProp"}}}`,
|
||||
}
|
||||
|
||||
for _, want := range tests {
|
||||
if !strings.Contains(json, want) {
|
||||
t.Errorf("Did not find expected snippet: %v", want)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Logf("Full JSON encoding:\n%v", json)
|
||||
}
|
||||
})
|
||||
|
||||
// Using stack.SerializeProperties will get the correct behavior and should be used
|
||||
// whenever persisting resources into some durable form.
|
||||
t.Run("SerializeProperties", func(t *testing.T) {
|
||||
serializedPropMap, err := SerializeProperties(propMap, config.BlindingCrypter)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Now JSON encode the results?
|
||||
b, err := json.Marshal(serializedPropMap)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshalling PropertyMap: %v", err)
|
||||
}
|
||||
json := string(b)
|
||||
|
||||
// Look for the specific JSON serialization of the properties.
|
||||
tests := []string{
|
||||
// Primitives
|
||||
`"bool":true`,
|
||||
`"string":"string literal"`,
|
||||
`"float32":2.5`,
|
||||
`"float64":1.5`,
|
||||
`"int32":41`,
|
||||
`"int64":42`,
|
||||
|
||||
// Data structures
|
||||
`"array":["a",true,32]`,
|
||||
`"array-empty":[]`,
|
||||
`"map":{"a":true,"b":88,"c":"c-see-saw","d":"d-dee-daw"}`,
|
||||
`"map-empty":{}`,
|
||||
|
||||
// Specialized resource types
|
||||
// nolint: lll
|
||||
`"asset-text":{"4dabf18193072939515e22adb298388d":"c44067f5952c0a294b673a41bacd8c17","hash":"64989ccbf3efa9c84e2afe7cee9bc5828bf0fcb91e44f8c1e591638a2c2e90e3","text":"alpha beta gamma"}`,
|
||||
|
||||
`"secret":{"4dabf18193072939515e22adb298388d":"1b47061264138c4ac30d75fd1eb44270","ciphertext":"[secret]"}`,
|
||||
}
|
||||
for _, want := range tests {
|
||||
if !strings.Contains(json, want) {
|
||||
t.Errorf("Did not find expected snippet: %v", want)
|
||||
}
|
||||
}
|
||||
|
||||
// Some properties are explicitly _not_ in serialized output.
|
||||
negativeTests := []string{
|
||||
`"nil"`,
|
||||
`"computed"`,
|
||||
`"output"`,
|
||||
}
|
||||
for _, doNotWant := range negativeTests {
|
||||
if strings.Contains(json, doNotWant) {
|
||||
t.Errorf("Found unexpected snippet: %v", doNotWant)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Logf("Full JSON encoding:\n%v", json)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue