pulumi/pkg/engine/detailedDiff_test.go

755 lines
19 KiB
Go

// Copyright 2019-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 engine
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
)
func TestTranslateDetailedDiff(t *testing.T) {
t.Parallel()
var (
A = plugin.PropertyDiff{Kind: plugin.DiffAdd}
D = plugin.PropertyDiff{Kind: plugin.DiffDelete}
U = plugin.PropertyDiff{Kind: plugin.DiffUpdate}
)
cases := []struct {
state map[string]interface{}
oldInputs map[string]interface{}
inputs map[string]interface{}
detailedDiff map[string]plugin.PropertyDiff
expected *resource.ObjectDiff
}{
{
state: map[string]interface{}{
"foo": 42,
},
inputs: map[string]interface{}{
"foo": 24,
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewNumberProperty(42),
New: resource.NewNumberProperty(24),
},
},
},
},
{
state: map[string]interface{}{
"foo": 42,
},
inputs: map[string]interface{}{
"foo": 42,
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewNumberProperty(42),
New: resource.NewNumberProperty(42),
},
},
},
},
{
state: map[string]interface{}{
"foo": 42,
"bar": "hello",
},
inputs: map[string]interface{}{
"foo": 24,
"bar": "hello",
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewNumberProperty(42),
New: resource.NewNumberProperty(24),
},
},
},
},
{
state: map[string]interface{}{
"foo": 42,
"bar": "hello",
},
inputs: map[string]interface{}{
"foo": 24,
"bar": "world",
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewNumberProperty(42),
New: resource.NewNumberProperty(24),
},
},
},
},
{
state: map[string]interface{}{},
inputs: map[string]interface{}{
"foo": 24,
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": A,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{
"foo": resource.NewNumberProperty(24),
},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{},
},
},
{
state: map[string]interface{}{
"foo": 24,
},
inputs: map[string]interface{}{},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": D,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{
"foo": resource.NewNumberProperty(24),
},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{},
},
},
{
state: map[string]interface{}{
"foo": 24,
},
oldInputs: map[string]interface{}{
"foo": 42,
},
inputs: map[string]interface{}{},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": {
Kind: plugin.DiffDelete,
InputDiff: true,
},
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{
"foo": resource.NewNumberProperty(42),
},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
"qux",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[1]": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{
1: {
Old: resource.NewStringProperty("baz"),
New: resource.NewStringProperty("qux"),
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
"qux",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewPropertyValue([]interface{}{
"bar",
"baz",
}),
New: resource.NewPropertyValue([]interface{}{
"bar",
"qux",
}),
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{
0: resource.NewPropertyValue("bar"),
},
Updates: map[int]resource.ValueDiff{
1: {
Old: resource.NewStringProperty("baz"),
New: resource.NewStringProperty("qux"),
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[1]": A,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{
1: resource.NewStringProperty("baz"),
},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[1]": D,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{
1: resource.NewStringProperty("baz"),
},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
"qux",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[100]": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{
100: {
Old: resource.PropertyValue{},
New: resource.PropertyValue{},
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
"bar",
"baz",
},
},
inputs: map[string]interface{}{
"foo": []interface{}{
"bar",
"qux",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[100][200]": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{
100: {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{
200: {
Old: resource.PropertyValue{},
New: resource.PropertyValue{},
},
},
},
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
map[string]interface{}{
"baz": 42,
},
},
},
inputs: map[string]interface{}{
"foo": []interface{}{},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[0].baz": D,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{
0: resource.NewObjectProperty(resource.PropertyMap{
"baz": resource.NewNumberProperty(42),
}),
},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "zed",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "alpha",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo.qux": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"qux": {
Old: resource.NewStringProperty("zed"),
New: resource.NewStringProperty("alpha"),
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "zed",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "alpha",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Old: resource.NewPropertyValue(map[string]interface{}{
"bar": "baz",
"qux": "zed",
}),
New: resource.NewPropertyValue(map[string]interface{}{
"bar": "baz",
"qux": "alpha",
}),
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{
"bar": resource.NewPropertyValue("baz"),
},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"qux": {
Old: resource.NewStringProperty("zed"),
New: resource.NewStringProperty("alpha"),
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "alpha",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo.qux": A,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{
"qux": resource.NewStringProperty("alpha"),
},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "zed",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo.qux": D,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{
"qux": resource.NewStringProperty("zed"),
},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "zed",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "alpha",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo.missing": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"missing": {
Old: resource.PropertyValue{},
New: resource.PropertyValue{},
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "zed",
},
},
inputs: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "baz",
"qux": "alpha",
},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo.nested.missing": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"nested": {
Object: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"missing": {
Old: resource.PropertyValue{},
New: resource.PropertyValue{},
},
},
},
},
},
},
},
},
},
},
{
state: map[string]interface{}{
"foo": []interface{}{
map[string]interface{}{
"baz": 42,
},
},
},
inputs: map[string]interface{}{
"foo": []interface{}{},
},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[0].baz": D,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo": {
Array: &resource.ArrayDiff{
Adds: map[int]resource.PropertyValue{},
Deletes: map[int]resource.PropertyValue{
0: resource.NewObjectProperty(resource.PropertyMap{
"baz": resource.NewNumberProperty(42),
}),
},
Sames: map[int]resource.PropertyValue{},
Updates: map[int]resource.ValueDiff{},
},
},
},
},
},
{
state: map[string]interface{}{},
inputs: map[string]interface{}{},
detailedDiff: map[string]plugin.PropertyDiff{
"foo[something.wonky]probably/miscalculated.by.provider": U,
},
expected: &resource.ObjectDiff{
Adds: resource.PropertyMap{},
Deletes: resource.PropertyMap{},
Sames: resource.PropertyMap{},
Updates: map[resource.PropertyKey]resource.ValueDiff{
"foo[something.wonky]probably/miscalculated.by.provider": {},
},
},
},
}
for _, c := range cases {
oldInputs := resource.NewPropertyMapFromMap(c.oldInputs)
state := resource.NewPropertyMapFromMap(c.state)
inputs := resource.NewPropertyMapFromMap(c.inputs)
diff := TranslateDetailedDiff(&StepEventMetadata{
Old: &StepEventStateMetadata{Inputs: oldInputs, Outputs: state},
New: &StepEventStateMetadata{Inputs: inputs},
DetailedDiff: c.detailedDiff,
}, false)
assert.Equal(t, c.expected, diff)
}
}