pulumi/pkg/resource/deploy/step_generator_test.go
Luke Hoban c3f5177f22
Fix rendering of diffs for resource without DetailedDiffs (#7500)
The diff rendering logic tests whether the DetailedDiff is nil to determine whether to use it for rendering or defer to older older supported approaches to computing diffs.

The new logic added in https://github.com/pulumi/pulumi/pull/7226 could lead to replacing a nil DetailedDiff with an empty DetailedDiff, whcih would make the rendering logic believe that a DetailedDiff was present but empty, instead of missing entirely.

This change ensures that we maintain nil-ness of the DetailedDiff when we transform it for handling of replaceOnChanges.

Also adds tests for this and other cases on applyReplaceOnChanges.

Fixes #7486.
2021-07-13 08:39:48 -06:00

197 lines
5.2 KiB
Go

package deploy
import (
"testing"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
"github.com/stretchr/testify/assert"
)
func TestIgnoreChanges(t *testing.T) {
cases := []struct {
name string
oldInputs map[string]interface{}
newInputs map[string]interface{}
expected map[string]interface{}
ignoreChanges []string
expectFailure bool
}{
{
name: "Present in old and new sets",
oldInputs: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
},
newInputs: map[string]interface{}{
"a": map[string]interface{}{
"b": "bar",
},
"c": 42,
},
expected: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"c": 42,
},
ignoreChanges: []string{"a.b"},
},
{
name: "Missing in new sets",
oldInputs: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
},
newInputs: map[string]interface{}{
"a": map[string]interface{}{},
"c": 42,
},
expected: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"c": 42,
},
ignoreChanges: []string{"a.b"},
},
{
name: "Missing in old deletes",
oldInputs: map[string]interface{}{},
newInputs: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
"c": 42,
},
expected: map[string]interface{}{
"a": map[string]interface{}{},
"c": 42,
},
ignoreChanges: []string{"a.b"},
},
{
name: "Missing keys in old and new are OK",
oldInputs: map[string]interface{}{},
newInputs: map[string]interface{}{},
ignoreChanges: []string{
"a",
"a.b",
"a.c[0]",
},
},
{
name: "Missing parent keys in only new fail",
oldInputs: map[string]interface{}{
"a": map[string]interface{}{
"b": "foo",
},
},
newInputs: map[string]interface{}{},
ignoreChanges: []string{"a.b"},
expectFailure: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
olds, news := resource.NewPropertyMapFromMap(c.oldInputs), resource.NewPropertyMapFromMap(c.newInputs)
expected := olds
if c.expected != nil {
expected = resource.NewPropertyMapFromMap(c.expected)
}
processed, res := processIgnoreChanges(news, olds, c.ignoreChanges)
if c.expectFailure {
assert.NotNil(t, res)
} else {
assert.Nil(t, res)
assert.Equal(t, expected, processed)
}
})
}
}
func TestApplyReplaceOnChangesEmptyDetailedDiff(t *testing.T) {
cases := []struct {
name string
diff plugin.DiffResult
replaceOnChanges []string
hasInitErrors bool
expected plugin.DiffResult
}{
{
name: "Empty diff and replaceOnChanges",
diff: plugin.DiffResult{},
replaceOnChanges: []string{},
hasInitErrors: false,
expected: plugin.DiffResult{},
},
{
name: "DiffSome and empty replaceOnChanges",
diff: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
replaceOnChanges: []string{},
hasInitErrors: false,
expected: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
},
{
name: "DiffSome and non-empty replaceOnChanges",
diff: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
replaceOnChanges: []string{"a"},
hasInitErrors: false,
expected: plugin.DiffResult{
Changes: plugin.DiffSome,
ChangedKeys: []resource.PropertyKey{"a"},
ReplaceKeys: []resource.PropertyKey{"a"},
},
},
{
name: "Empty diff and replaceOnChanges w/ init errors",
diff: plugin.DiffResult{},
replaceOnChanges: []string{},
hasInitErrors: true,
expected: plugin.DiffResult{},
},
{
name: "DiffSome and empty replaceOnChanges w/ init errors",
diff: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
replaceOnChanges: []string{},
hasInitErrors: true,
expected: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
},
{
name: "DiffSome and non-empty replaceOnChanges w/ init errors",
diff: plugin.DiffResult{Changes: plugin.DiffSome, ChangedKeys: []resource.PropertyKey{"a"}},
replaceOnChanges: []string{"a"},
hasInitErrors: true,
expected: plugin.DiffResult{
Changes: plugin.DiffSome,
ChangedKeys: []resource.PropertyKey{"a"},
ReplaceKeys: []resource.PropertyKey{"a"},
},
},
{
name: "Empty diff and non-empty replaceOnChanges w/ init errors",
diff: plugin.DiffResult{},
replaceOnChanges: []string{"*"},
hasInitErrors: true,
expected: plugin.DiffResult{
Changes: plugin.DiffSome,
ReplaceKeys: []resource.PropertyKey{"#initerror"},
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
newdiff, err := applyReplaceOnChanges(c.diff, c.replaceOnChanges, c.hasInitErrors)
assert.NoError(t, err)
assert.Equal(t, c.expected, newdiff)
})
}
}