From d6030386faca5480bda53279ee6367c89df7e2bf Mon Sep 17 00:00:00 2001 From: Venelin Date: Wed, 11 Dec 2024 17:42:17 +0000 Subject: [PATCH] Refactor SDKv2 non-set detailed diff tests --- pkg/tests/detailed_diff_list_test.go | 221 ++ pkg/tests/detailed_diff_test.go | 2187 +---------------- pkg/tests/detailed_diff_unknown_test.go | 1058 ++++++++ .../list_attribute/added_empty.golden | 15 + .../list_attribute/added_non-empty.golden | 37 + .../list_attribute/changed.golden | 38 + .../list_element_added_back.golden | 45 + .../list_element_added_front.golden | 51 + .../list_element_added_middle.golden | 49 + .../list_element_removed_end.golden | 52 + .../list_element_removed_front.golden | 50 + .../list_element_removed_middle.golden | 52 + .../list_attribute/removed_empty.golden | 15 + .../list_attribute/removed_non-empty.golden | 37 + .../list_attribute/unchanged_empty.golden | 11 + .../list_attribute/unchanged_non-empty.golden | 18 + .../list_block/added_empty.golden | 15 + .../list_block/added_non-empty.golden | 40 + .../list_block/changed.golden | 41 + .../list_block/list_element_added_back.golden | 48 + .../list_element_added_front.golden | 62 + .../list_element_added_middle.golden | 57 + .../list_element_removed_end.golden | 62 + .../list_element_removed_front.golden | 57 + .../list_element_removed_middle.golden | 62 + .../list_block/removed_empty.golden | 15 + .../list_block/removed_non-empty.golden | 40 + .../list_block/unchanged_empty.golden | 11 + .../list_block/unchanged_non-empty.golden | 18 + .../added_empty.golden | 15 + .../added_non-empty.golden | 35 + .../max_items_one_attribute/changed.golden | 36 + .../removed_empty.golden | 15 + .../removed_non-empty.golden | 35 + .../unchanged_empty.golden | 11 + .../unchanged_non-empty.golden | 18 + .../max_items_one_block/added_empty.golden | 15 + .../added_non-empty.golden | 38 + .../max_items_one_block/changed.golden | 39 + .../max_items_one_block/removed_empty.golden | 15 + .../removed_non-empty.golden | 38 + .../unchanged_empty.golden | 11 + .../unchanged_non-empty.golden | 18 + .../testdata/TestDetailedDiffMap/added.golden | 36 + .../TestDetailedDiffMap/key_changed.golden | 43 + .../TestDetailedDiffMap/removed.golden | 38 + .../unchanged_empty.golden | 16 + .../unchanged_non-empty.golden | 18 + .../TestDetailedDiffMap/value_changed.golden | 38 + .../TestDetailedDiffString/added.golden | 30 + .../TestDetailedDiffString/changed.golden | 31 + .../TestDetailedDiffString/removed.golden | 30 + .../unchanged_empty.golden | 11 + .../unchanged_non-empty.golden | 15 + 54 files changed, 3019 insertions(+), 2090 deletions(-) create mode 100644 pkg/tests/detailed_diff_list_test.go create mode 100644 pkg/tests/detailed_diff_unknown_test.go create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_back.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_front.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_middle.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_end.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_front.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_middle.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/added_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/added_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_back.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_front.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_middle.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_end.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_front.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_middle.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/removed_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/removed_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/added.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/key_changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/removed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/unchanged_non-empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffMap/value_changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffString/added.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffString/changed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffString/removed.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffString/unchanged_empty.golden create mode 100644 pkg/tests/testdata/TestDetailedDiffString/unchanged_non-empty.golden diff --git a/pkg/tests/detailed_diff_list_test.go b/pkg/tests/detailed_diff_list_test.go new file mode 100644 index 000000000..493cc6258 --- /dev/null +++ b/pkg/tests/detailed_diff_list_test.go @@ -0,0 +1,221 @@ +package tests + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hexops/autogold/v2" + "github.com/zclconf/go-cty/cty" + + crosstests "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests" +) + +func TestDetailedDiffList(t *testing.T) { + t.Parallel() + + listAttrSchema := schema.Resource{ + Schema: map[string]*schema.Schema{ + "list_attr": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } + + maxItemsOneAttrSchema := schema.Resource{ + Schema: map[string]*schema.Schema{ + "list_attr": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } + + listBlockSchema := schema.Resource{ + Schema: map[string]*schema.Schema{ + "list_block": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prop": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } + + maxItemsOneBlockSchema := schema.Resource{ + Schema: map[string]*schema.Schema{ + "list_block": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_prop": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } + + attrList := func(arr *[]string) map[string]cty.Value { + if arr == nil { + return map[string]cty.Value{} + } + + if len(*arr) == 0 { + return map[string]cty.Value{ + "list_attr": cty.ListValEmpty(cty.String), + } + } + + slice := make([]cty.Value, len(*arr)) + for i, v := range *arr { + slice[i] = cty.StringVal(v) + } + return map[string]cty.Value{ + "list_attr": cty.ListVal(slice), + } + } + + blockList := func(arr *[]string) map[string]cty.Value { + if arr == nil { + return map[string]cty.Value{} + } + + if len(*arr) == 0 { + return map[string]cty.Value{ + "list_block": cty.ListValEmpty(cty.DynamicPseudoType), + } + } + + slice := make([]cty.Value, len(*arr)) + for i, v := range *arr { + slice[i] = cty.ObjectVal(map[string]cty.Value{"prop": cty.StringVal(v)}) + } + return map[string]cty.Value{ + "list_block": cty.ListVal(slice), + } + } + + nestedBlockList := func(arr *[]string) map[string]cty.Value { + if arr == nil { + return map[string]cty.Value{} + } + + if len(*arr) == 0 { + return map[string]cty.Value{ + "list_block": cty.ListValEmpty(cty.DynamicPseudoType), + } + } + + slice := make([]cty.Value, len(*arr)) + for i, v := range *arr { + slice[i] = cty.ObjectVal(map[string]cty.Value{"nested_prop": cty.StringVal(v)}) + } + return map[string]cty.Value{ + "list_block": cty.ListVal(slice), + } + } + + listPairs := []struct { + name string + schema schema.Resource + valueMaker func(*[]string) map[string]cty.Value + }{ + {"list attribute", listAttrSchema, attrList}, + {"list block", listBlockSchema, blockList}, + } + + maxItemsOnePairs := []struct { + name string + schema schema.Resource + valueMaker func(*[]string) map[string]cty.Value + }{ + {"max items one attribute", maxItemsOneAttrSchema, attrList}, + {"max items one block", maxItemsOneBlockSchema, nestedBlockList}, + } + + oneElementScenarios := []struct { + name string + initialValue *[]string + changeValue *[]string + }{ + {"unchanged empty", nil, nil}, + {"unchanged non-empty", ref([]string{"val1"}), ref([]string{"val1"})}, + {"added non-empty", nil, ref([]string{"val1"})}, + {"added empty", nil, ref([]string{})}, + {"removed non-empty", ref([]string{"val1"}), nil}, + {"removed empty", ref([]string{}), nil}, + {"changed", ref([]string{"val1"}), ref([]string{"val2"})}, + } + + multiElementScenarios := []struct { + name string + initialValue *[]string + changeValue *[]string + }{ + {"list element added front", ref([]string{"val2", "val3"}), ref([]string{"val1", "val2", "val3"})}, + {"list element added back", ref([]string{"val1", "val2"}), ref([]string{"val1", "val2", "val3"})}, + {"list element added middle", ref([]string{"val1", "val3"}), ref([]string{"val1", "val2", "val3"})}, + {"list element removed front", ref([]string{"val1", "val2", "val3"}), ref([]string{"val3", "val2"})}, + {"list element removed middle", ref([]string{"val1", "val2", "val3"}), ref([]string{"val3", "val1"})}, + {"list element removed end", ref([]string{"val1", "val2", "val3"}), ref([]string{"val2", "val1"})}, + } + + scenarios := append(oneElementScenarios, multiElementScenarios...) + + type testOutput struct { + initialValue *[]string + changeValue *[]string + tfOut string + pulumiOut string + detailedDiff map[string]any + } + + runTest := func(t *testing.T, schema schema.Resource, valueMaker func(*[]string) map[string]cty.Value, initialValue *[]string, changeValue *[]string) { + diff := crosstests.Diff(t, &schema, valueMaker(initialValue), valueMaker(changeValue)) + autogold.ExpectFile(t, testOutput{ + initialValue: initialValue, + changeValue: changeValue, + tfOut: diff.TFOut, + pulumiOut: diff.PulumiOut, + detailedDiff: diff.PulumiDiff.DetailedDiff, + }) + } + + for _, schemaValueMakerPair := range listPairs { + t.Run(schemaValueMakerPair.name, func(t *testing.T) { + t.Parallel() + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + t.Parallel() + runTest(t, schemaValueMakerPair.schema, schemaValueMakerPair.valueMaker, scenario.initialValue, scenario.changeValue) + }) + } + }) + } + + for _, schemaValueMakerPair := range maxItemsOnePairs { + t.Run(schemaValueMakerPair.name, func(t *testing.T) { + t.Parallel() + for _, scenario := range oneElementScenarios { + t.Run(scenario.name, func(t *testing.T) { + t.Parallel() + runTest(t, schemaValueMakerPair.schema, schemaValueMakerPair.valueMaker, scenario.initialValue, scenario.changeValue) + }) + } + }) + } +} diff --git a/pkg/tests/detailed_diff_test.go b/pkg/tests/detailed_diff_test.go index 686a4006b..df5a500f4 100644 --- a/pkg/tests/detailed_diff_test.go +++ b/pkg/tests/detailed_diff_test.go @@ -1,2134 +1,141 @@ package tests import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hexops/autogold/v2" - "github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview" - "github.com/stretchr/testify/require" + "github.com/zclconf/go-cty/cty" - "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/pulcheck" - "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" + crosstests "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/cross-tests" ) -func TestUnknownHandling(t *testing.T) { - t.Parallel() - resMap := map[string]*schema.Resource{ - "prov_test": { - Schema: map[string]*schema.Schema{ - "test": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - "prov_aux": { - Schema: map[string]*schema.Schema{ - "aux": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - }, - CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { - d.SetId("aux") - err := d.Set("aux", "aux") - require.NoError(t, err) - return nil - }, - }, - } - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp) - program := ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - mainRes: - type: prov:index:Test - properties: - test: ${auxRes.aux} -outputs: - testOut: ${mainRes.test} -` - pt := pulcheck.PulCheck(t, bridgedProvider, program) - res := pt.Preview(t, optpreview.Diff()) - // Test that the test property is unknown at preview time - require.Contains(t, res.StdOut, "test : output") - resUp := pt.Up(t) - // assert that the property gets resolved - require.Equal(t, "aux", resUp.Outputs["testOut"].Value) -} - -func trimDiff(t *testing.T, diff string) string { - urnLine := " [urn=urn:pulumi:test::test::pulumi:pulumi:Stack::test-test]" - resourcesSummaryLine := "Resources:\n" - require.Contains(t, diff, urnLine) - require.Contains(t, diff, resourcesSummaryLine) - - // trim the diff to only include the contents after the URN line and before the summary - urnIndex := strings.Index(diff, urnLine) - resourcesSummaryIndex := strings.Index(diff, resourcesSummaryLine) - return diff[urnIndex+len(urnLine) : resourcesSummaryIndex] +func ref[T any](v T) *T { + return &v } -func TestUnknownBlocks(t *testing.T) { +func TestDetailedDiffString(t *testing.T) { t.Parallel() - resMap := map[string]*schema.Resource{ - "prov_test": { - Schema: map[string]*schema.Schema{ - "test": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test_prop": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - }, - "prov_nested_test": { - Schema: map[string]*schema.Schema{ - "test": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "nested_prop": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test_prop": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - "prov_aux": { - Schema: map[string]*schema.Schema{ - "aux": { - Type: schema.TypeList, - Computed: true, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test_prop": { - Type: schema.TypeString, - Computed: true, - Optional: true, - }, - }, - }, - }, - "nested_aux": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "nested_prop": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test_prop": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - }, - }, - }, - }, - CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { - d.SetId("aux") - if d.Get("aux") == nil { - err := d.Set("aux", []map[string]interface{}{{"test_prop": "aux"}}) - require.NoError(t, err) - } - if d.Get("nested_aux") == nil { - err := d.Set("nested_aux", []map[string]interface{}{ - { - "nested_prop": []map[string]interface{}{ - {"test_prop": []string{"aux"}}, - }, - }, - }) - require.NoError(t, err) - } - return nil + + res := schema.Resource{ + Schema: map[string]*schema.Schema{ + "string_prop": { + Type: schema.TypeString, + Optional: true, }, }, } - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp) - provTestKnownProgram := ` -name: test -runtime: yaml -resources: - mainRes: - type: prov:index:Test - properties: - tests: - - testProp: "known_val" -` - nestedProvTestKnownProgram := ` -name: test -runtime: yaml -resources: - mainRes: - type: prov:index:NestedTest - properties: - tests: - - nestedProps: - - testProps: - - "known_val" -` + valueOne := ref("val1") + valueTwo := ref("val2") + var noValue *string - for _, tc := range []struct { - name string - program string - initialKnownProgram string - expectedInitial autogold.Value - expectedUpdate autogold.Value + ctyVal := func(v *string) map[string]cty.Value { + if v == nil { + return map[string]cty.Value{} + } + return map[string]cty.Value{ + "string_prop": cty.StringVal(*v), + } + } + + scenarios := []struct { + name string + initialValue *string + changeValue *string }{ - { - "list of objects", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:Test - properties: - tests: ${auxRes.auxes} -`, - provTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/test:Test: (create) - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - tests : output -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - tests: [ - - [0]: { - - testProp: "known_val" - } - ] - + tests: output -`), - }, - { - "unknown object", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:Test - properties: - tests: - - ${auxRes.auxes[0]} -`, - provTestKnownProgram, + {"unchanged empty", noValue, noValue}, + {"unchanged non-empty", valueOne, valueOne}, + {"added", noValue, valueOne}, + {"removed", valueOne, noValue}, + {"changed", valueOne, valueTwo}, + } - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/test:Test: (create) - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - tests : [ - [0]: output - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - testProp: "known_val" - } - + [0]: output - ] -`), - }, - { - "unknown object with others", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:Test - properties: - tests: - - ${auxRes.auxes[0]} - - {"testProp": "val"} -`, - provTestKnownProgram, + type testOutput struct { + initialValue *string + changeValue *string + tfOut string + pulumiOut string + detailedDiff map[string]any + } - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/test:Test: (create) - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - tests : [ - [0]: output - [1]: { - testProp : "val" - } - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - testProp: "known_val" - } - + [0]: output - + [1]: { - + testProp : "val" - } - ] -`), - }, - { - "unknown nested", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: ${auxRes.nestedAuxes} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : output -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - - tests: [ - - [0]: { - - nestedProps: [ - - [0]: { - - testProps: [ - - [0]: "known_val" - ] - } - ] - } - ] - + tests: output -`), - }, - { - "unknown nested level 1", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: - - ${auxRes.nestedAuxes[0]} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : [ - [0]: output - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - ~ tests: [ - - [0]: { - - nestedProps: [ - - [0]: { - - testProps: [ - - [0]: "known_val" - ] - } - ] - } - + [0]: output - ] -`), - }, - { - "unknown nested level 2", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: - - nestedProps: ${auxRes.nestedAuxes[0].nestedProps} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : [ - [0]: { - nestedProps: output - } - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - ~ tests: [ - ~ [0]: { - - nestedProps: [ - - [0]: { - - testProps: [ - - [0]: "known_val" - ] - } - ] - + nestedProps: output - } - ] -`), - }, - { - "unknown nested level 3", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: - - nestedProps: - - ${auxRes.nestedAuxes[0].nestedProps[0]} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : [ - [0]: { - nestedProps: [ - [0]: output - ] - } - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - ~ tests: [ - ~ [0]: { - ~ nestedProps: [ - - [0]: { - - testProps: [ - - [0]: "known_val" - ] - } - + [0]: output - ] - } - ] -`), - }, - { - "unknown nested level 4", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: - - nestedProps: - - testProps: ${auxRes.nestedAuxes[0].nestedProps[0].testProps} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : [ - [0]: { - nestedProps: [ - [0]: { - testProps : output - } - ] - } - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - ~ tests: [ - ~ [0]: { - ~ nestedProps: [ - ~ [0]: { - - testProps: [ - - [0]: "known_val" - ] - + testProps: output - } - ] - } - ] -`), - }, - { - "unknown nested level 5", - ` -name: test -runtime: yaml -resources: - auxRes: - type: prov:index:Aux - properties: - auxes: %s - nestedAuxes: %s - mainRes: - type: prov:index:NestedTest - properties: - tests: - - nestedProps: - - testProps: - - ${auxRes.nestedAuxes[0].nestedProps[0].testProps[0]} -`, - nestedProvTestKnownProgram, - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - + prov:index/nestedTest:NestedTest: (create) - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - tests : [ - [0]: { - nestedProps: [ - [0]: { - testProps : [ - [0]: output - ] - } - ] - } - ] -`), - autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/nestedTest:NestedTest: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] - ~ tests: [ - ~ [0]: { - ~ nestedProps: [ - ~ [0]: { - ~ testProps: [ - ~ [0]: "known_val" => output - ] - } - ] - } - ] -`), - }, - } { - t.Run(tc.name, func(t *testing.T) { + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { t.Parallel() - computedProgram := fmt.Sprintf(tc.program, "null", "null") - - t.Run("initial preview", func(t *testing.T) { - pt := pulcheck.PulCheck(t, bridgedProvider, computedProgram) - res := pt.Preview(t, optpreview.Diff()) - t.Log(res.StdOut) - - tc.expectedInitial.Equal(t, trimDiff(t, res.StdOut)) - }) - - t.Run("update preview", func(t *testing.T) { - t.Parallel() - t.Skipf("Skipping this test as it this case is not handled by the TF plugin sdk") - // The TF plugin SDK does not handle removing an input for a computed value, even if the provider implements it. - // The plugin SDK always fills an empty Computed property with the value from the state. - // Diff in these cases always returns no diff and the old state value is used. - nonComputedProgram := fmt.Sprintf(tc.program, "[{testProp: \"val1\"}]", "[{nestedProps: [{testProps: [\"val1\"]}]}]") - pt := pulcheck.PulCheck(t, bridgedProvider, nonComputedProgram) - pt.Up(t) - - pulumiYamlPath := filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml") - - err := os.WriteFile(pulumiYamlPath, []byte(computedProgram), 0o600) - require.NoError(t, err) - - res := pt.Preview(t, optpreview.Diff()) - t.Log(res.StdOut) - tc.expectedUpdate.Equal(t, trimDiff(t, res.StdOut)) - }) - - t.Run("update preview with computed", func(t *testing.T) { - t.Parallel() - pt := pulcheck.PulCheck(t, bridgedProvider, tc.initialKnownProgram) - pt.Up(t) - - pulumiYamlPath := filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml") - - err := os.WriteFile(pulumiYamlPath, []byte(computedProgram), 0o600) - require.NoError(t, err) - - res := pt.Preview(t, optpreview.Diff()) - t.Log(res.StdOut) - tc.expectedUpdate.Equal(t, trimDiff(t, res.StdOut)) + diff := crosstests.Diff(t, &res, ctyVal(scenario.initialValue), ctyVal(scenario.changeValue)) + autogold.ExpectFile(t, testOutput{ + initialValue: scenario.initialValue, + changeValue: scenario.changeValue, + tfOut: diff.TFOut, + pulumiOut: diff.PulumiOut, + detailedDiff: diff.PulumiDiff.DetailedDiff, }) }) } } -func TestDetailedDiffPlainTypes(t *testing.T) { +func TestDetailedDiffMap(t *testing.T) { t.Parallel() - resMap := map[string]*schema.Resource{ - "prov_test": { - Schema: map[string]*schema.Schema{ - "string_prop": { - Type: schema.TypeString, - Optional: true, - }, - "list_prop": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "set_prop": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "map_prop": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "list_block": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "set_block": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "max_items_one_block": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, + + res := schema.Resource{ + Schema: map[string]*schema.Schema{ + "map_prop": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, }, } - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) - - program := ` -name: test -runtime: yaml -resources: - mainRes: - type: prov:index:Test - properties: %s -` - - for _, tc := range []struct { - name string - props1 interface{} - props2 interface{} - expected autogold.Value - }{ - { - "string unchanged", - map[string]interface{}{"stringProp": "val"}, - map[string]interface{}{"stringProp": "val"}, - autogold.Expect("\n"), - }, - { - "string added", - map[string]interface{}{}, - map[string]interface{}{"stringProp": "val"}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + stringProp: "val" -`), - }, - { - "string removed", - map[string]interface{}{"stringProp": "val1"}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - stringProp: "val1" -`), - }, - { - "string changed", - map[string]interface{}{"stringProp": "val1"}, - map[string]interface{}{"stringProp": "val2"}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ stringProp: "val1" => "val2" -`), - }, - { - "list unchanged", - map[string]interface{}{"listProps": []interface{}{"val"}}, - map[string]interface{}{"listProps": []interface{}{"val"}}, - autogold.Expect("\n"), - }, - { - "list added", - map[string]interface{}{}, - map[string]interface{}{"listProps": []interface{}{"val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + listProps: [ - + [0]: "val" - ] -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "list added empty", - map[string]interface{}{}, - map[string]interface{}{"listProps": []interface{}{}}, - autogold.Expect("\n"), - }, - { - "list removed", - map[string]interface{}{"listProps": []interface{}{"val"}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - listProps: [ - - [0]: "val" - ] -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "list removed empty", - map[string]interface{}{"listProps": []interface{}{}}, - map[string]interface{}{}, - autogold.Expect("\n"), - }, - { - "list element added front", - map[string]interface{}{"listProps": []interface{}{"val2", "val3"}}, - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - ~ [0]: "val2" => "val1" - ~ [1]: "val3" => "val2" - + [2]: "val3" - ] -`), - }, - { - "list element added back", - map[string]interface{}{"listProps": []interface{}{"val1", "val2"}}, - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - + [2]: "val3" - ] -`), - }, - { - "list element added middle", - map[string]interface{}{"listProps": []interface{}{"val1", "val3"}}, - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - ~ [1]: "val3" => "val2" - + [2]: "val3" - ] -`), - }, - { - "list element removed front", - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"listProps": []interface{}{"val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - ~ [0]: "val1" => "val2" - ~ [1]: "val2" => "val3" - - [2]: "val3" - ] -`), - }, - { - "list element removed back", - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"listProps": []interface{}{"val1", "val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - - [2]: "val3" - ] -`), - }, - { - "list element removed middle", - map[string]interface{}{"listProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"listProps": []interface{}{"val1", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - ~ [1]: "val2" => "val3" - - [2]: "val3" - ] -`), - }, - { - "list element changed", - map[string]interface{}{"listProps": []interface{}{"val1"}}, - map[string]interface{}{"listProps": []interface{}{"val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listProps: [ - ~ [0]: "val1" => "val2" - ] -`), - }, - { - "set unchanged", - map[string]interface{}{"setProps": []interface{}{"val"}}, - map[string]interface{}{"setProps": []interface{}{"val"}}, - autogold.Expect("\n"), - }, - { - "set added", - map[string]interface{}{}, - map[string]interface{}{"setProps": []interface{}{"val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + setProps: [ - + [0]: "val" - ] -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "set added empty", - map[string]interface{}{}, - map[string]interface{}{"setProps": []interface{}{}}, - autogold.Expect("\n"), - }, - { - "set removed", - map[string]interface{}{"setProps": []interface{}{"val"}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - setProps: [ - - [0]: "val" - ] -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "set removed empty", - map[string]interface{}{"setProps": []interface{}{}}, - map[string]interface{}{}, - autogold.Expect("\n"), - }, - { - "set element added front", - map[string]interface{}{"setProps": []interface{}{"val2", "val3"}}, - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - + [0]: "val1" - ] -`), - }, - { - "set element added back", - map[string]interface{}{"setProps": []interface{}{"val1", "val2"}}, - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - + [2]: "val3" - ] -`), - }, - { - "set element added middle", - map[string]interface{}{"setProps": []interface{}{"val1", "val3"}}, - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - + [1]: "val2" - ] -`), - }, - { - "set element removed front", - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"setProps": []interface{}{"val2", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - - [0]: "val1" - ] -`), - }, - { - "set element removed back", - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"setProps": []interface{}{"val1", "val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - - [2]: "val3" - ] -`), - }, - { - "set element removed middle", - map[string]interface{}{"setProps": []interface{}{"val1", "val2", "val3"}}, - map[string]interface{}{"setProps": []interface{}{"val1", "val3"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - - [1]: "val2" - ] -`), - }, - { - "set element changed", - map[string]interface{}{"setProps": []interface{}{"val1"}}, - map[string]interface{}{"setProps": []interface{}{"val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setProps: [ - ~ [0]: "val1" => "val2" - ] -`), - }, - { - "map unchanged", - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - autogold.Expect("\n"), - }, - { - "map added", - map[string]interface{}{}, - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + mapProp: { - + key: "val" - } -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "map added empty", - map[string]interface{}{}, - map[string]interface{}{"mapProp": map[string]interface{}{}}, - autogold.Expect("\n"), - }, - { - "map removed", - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - mapProp: { - - key: "val" - } -`), - }, - // pulumi/pulumi-terraform-bridge#2233: This is the intended behavior - { - "map removed empty", - map[string]interface{}{"mapProp": map[string]interface{}{}}, - map[string]interface{}{}, - autogold.Expect("\n"), - }, - { - "map element added", - map[string]interface{}{"mapProp": map[string]interface{}{}}, - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + mapProp: { - + key: "val" - } -`), - }, - { - "map element removed", - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val"}}, - map[string]interface{}{"mapProp": map[string]interface{}{}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ mapProp: { - - key: "val" - } -`), - }, - { - "map value changed", - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val1"}}, - map[string]interface{}{"mapProp": map[string]interface{}{"key": "val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ mapProp: { - ~ key: "val1" => "val2" - } -`), - }, - { - "map key changed", - map[string]interface{}{"mapProp": map[string]interface{}{"key1": "val"}}, - map[string]interface{}{"mapProp": map[string]interface{}{"key2": "val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ mapProp: { - - key1: "val" - + key2: "val" - } -`), - }, - { - "list block unchanged", - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - autogold.Expect("\n"), - }, - { - "list block added", - map[string]interface{}{}, - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + listBlocks: [ - + [0]: { - + prop : "val" - } - ] -`), - }, - // This is expected to be a no-op because blocks can not be nil in TF - { - "list block added empty", - map[string]interface{}{}, - map[string]interface{}{"listBlocks": []interface{}{}}, - autogold.Expect("\n"), - }, - { - "list block added empty object", - map[string]interface{}{}, - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{}}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + listBlocks: [ - + [0]: { - } - ] -`), - }, - { - "list block removed", - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - listBlocks: [ - - [0]: { - - prop: "val" - } - ] -`), - }, - // This is expected to be a no-op because blocks can not be nil in TF - { - "list block removed empty", - map[string]interface{}{"listBlocks": []interface{}{}}, - map[string]interface{}{}, - autogold.Expect("\n"), - }, - { - "list block removed empty object", - map[string]interface{}{"listBlocks": []interface{}{map[string]interface{}{}}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - listBlocks: [ - - [0]: { - - prop: - } - ] -`), - }, - { - "list block element added front", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - ~ [0]: { - ~ prop: "val2" => "val1" - } - ~ [1]: { - ~ prop: "val3" => "val2" - } - + [2]: { - + prop : "val3" - } - ] -`), - }, - { - "list block element added back", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - + [2]: { - + prop : "val3" - } - ] -`), - }, - { - "list block element added middle", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - ~ [1]: { - ~ prop: "val3" => "val2" - } - + [2]: { - + prop : "val3" - } - ] -`), - }, - { - "list block element removed front", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - ~ [0]: { - ~ prop: "val1" => "val2" - } - ~ [1]: { - ~ prop: "val2" => "val3" - } - - [2]: { - - prop: "val3" - } - ] -`), - }, - { - "list block element removed back", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - - [2]: { - - prop: "val3" - } - ] -`), - }, - { - "list block element removed middle", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - ~ [1]: { - ~ prop: "val2" => "val3" - } - - [2]: { - - prop: "val3" - } - ] -`), - }, - { - "list block element changed", - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - }}, - map[string]interface{}{"listBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ listBlocks: [ - ~ [0]: { - ~ prop: "val1" => "val2" - } - ] -`), - }, - { - "set block unchanged", - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - autogold.Expect("\n"), - }, - { - "set block added", - map[string]interface{}{}, - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + setBlocks: [ - + [0]: { - + prop : "val" - } - ] -`), - }, - // This is expected to be a no-op because blocks can not be nil in TF - { - "set block added empty", - map[string]interface{}{}, - map[string]interface{}{"setBlocks": []interface{}{}}, - autogold.Expect("\n"), - }, - { - "set block added empty object", - map[string]interface{}{}, - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{}}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + setBlocks: [ - + [0]: { - } - ] -`), - }, - { - "set block removed", - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{"prop": "val"}}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - setBlocks: [ - - [0]: { - - prop: "val" - } - ] -`), - }, - // This is expected to be a no-op because blocks can not be nil in TF - { - "set block removed empty", - map[string]interface{}{"setBlocks": []interface{}{}}, - map[string]interface{}{}, - autogold.Expect("\n"), - }, - // TODO[pulumi/pulumi-terraform-bridge#2399] nested prop diff - { - "set block removed empty object", - map[string]interface{}{"setBlocks": []interface{}{map[string]interface{}{}}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - setBlocks: [ - - [0]: { - - prop: "" - } - ] -`), - }, - { - "set block element added front", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - + [0]: { - + prop : "val1" - } - ] -`), - }, - { - "set block element added back", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - + [2]: { - + prop : "val3" - } - ] -`), - }, - { - "set block element added middle", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - + [1]: { - + prop : "val2" - } - ] -`), - }, - { - "set block element removed front", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - - [0]: { - - prop: "val1" - } - ] -`), - }, - { - "set block element removed back", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - - [2]: { - - prop: "val3" - } - ] -`), - }, - { - "set block element removed middle", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val2"}, - map[string]interface{}{"prop": "val3"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - map[string]interface{}{"prop": "val3"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - - [1]: { - - prop: "val2" - } - ] -`), - }, - { - "set block element changed", - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val1"}, - }}, - map[string]interface{}{"setBlocks": []interface{}{ - map[string]interface{}{"prop": "val2"}, - }}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ setBlocks: [ - ~ [0]: { - ~ prop: "val1" => "val2" - } - ] -`), - }, - { - "maxItemsOne block unchanged", - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val"}}, - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val"}}, - autogold.Expect("\n"), - }, - { - "maxItemsOne block added", - map[string]interface{}{}, - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + maxItemsOneBlock: { - + prop : "val" - } -`), - }, - { - "maxItemsOne block added empty", - map[string]interface{}{}, - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - + maxItemsOneBlock: { - } -`), - }, - { - "maxItemsOne block removed", - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val"}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - maxItemsOneBlock: { - - prop: "val" - } -`), - }, - // TODO[pulumi/pulumi-terraform-bridge#2399] nested prop diff - { - "maxItemsOne block removed empty", - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{}}, - map[string]interface{}{}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - maxItemsOneBlock: { - - prop: - } -`), - }, - { - "maxItemsOne block changed", - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val1"}}, - map[string]interface{}{"maxItemsOneBlock": map[string]interface{}{"prop": "val2"}}, - autogold.Expect(` - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ maxItemsOneBlock: { - ~ prop: "val1" => "val2" - } -`), - }, - } { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - props1, err := json.Marshal(tc.props1) - require.NoError(t, err) - program1 := fmt.Sprintf(program, string(props1)) - props2, err := json.Marshal(tc.props2) - require.NoError(t, err) - program2 := fmt.Sprintf(program, string(props2)) - pt := pulcheck.PulCheck(t, bridgedProvider, program1) - pt.Up(t) - - pulumiYamlPath := filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml") - - err = os.WriteFile(pulumiYamlPath, []byte(program2), 0o600) - require.NoError(t, err) - res := pt.Preview(t, optpreview.Diff()) - t.Log(res.StdOut) - tc.expected.Equal(t, trimDiff(t, res.StdOut)) - }) - } -} + ctyVal := func(v map[string]string) map[string]cty.Value { + ctyMap := make(map[string]cty.Value) -func TestUnknownCollectionForceNewDetailedDiff(t *testing.T) { - t.Parallel() - collectionForceNewResource := func(typ schema.ValueType) *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test": { - Type: typ, - Optional: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, + if len(v) == 0 { + return map[string]cty.Value{ + "map_prop": cty.MapValEmpty(cty.String), + } } - } - propertyForceNewResource := func(typ schema.ValueType) *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "test": { - Type: typ, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - }, - }, - }, - }, + for k, v := range v { + ctyMap[k] = cty.StringVal(v) } - } - - auxResource := func(typ schema.ValueType) *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "aux": { - Type: typ, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prop": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - }, - CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { - d.SetId("aux") - err := d.Set("aux", []map[string]interface{}{{"prop": "aux"}}) - require.NoError(t, err) - return nil - }, + return map[string]cty.Value{ + "map_prop": cty.MapVal(ctyMap), } } - initialProgram := ` - name: test - runtime: yaml - resources: - mainRes: - type: prov:index:Test - properties: - tests: [{prop: 'value'}] -` - - program := ` - name: test - runtime: yaml - resources: - auxRes: - type: prov:index:Aux - mainRes: - type: prov:index:Test - properties: - tests: %s -` - - runTest := func(t *testing.T, program2 string, bridgedProvider info.Provider, expectedOutput autogold.Value) { - pt := pulcheck.PulCheck(t, bridgedProvider, initialProgram) - pt.Up(t) - pt.WritePulumiYaml(t, program2) - - res := pt.Preview(t, optpreview.Diff()) - - expectedOutput.Equal(t, trimDiff(t, res.StdOut)) + scenarios := []struct { + name string + initialValue map[string]string + changeValue map[string]string + }{ + {"unchanged empty", map[string]string{}, map[string]string{}}, + {"unchanged non-empty", map[string]string{"key": "val"}, map[string]string{"key": "val"}}, + {"added", map[string]string{}, map[string]string{"key": "val"}}, + {"removed", map[string]string{"key": "val"}, map[string]string{}}, + {"value changed", map[string]string{"key": "val"}, map[string]string{"key": "val2"}}, + {"key changed", map[string]string{"key": "val"}, map[string]string{"key2": "val"}}, } - t.Run("list force new", func(t *testing.T) { - resMap := map[string]*schema.Resource{ - "prov_test": collectionForceNewResource(schema.TypeList), - "prov_aux": auxResource(schema.TypeList), - } - - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) - runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { - runTest(t, program2, bridgedProvider, expectedOutput) - } - - t.Run("unknown plain property", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - ~ [0]: { - ~ prop: "value" => output - } - ] -`)) - }) - - t.Run("unknown object", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - prop: "value" - } - + [0]: output - ] -`)) - }) - - t.Run("unknown collection", func(t *testing.T) { - program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - tests: [ - - [0]: { - - prop: "value" - } - ] - + tests: output -`)) - }) - }) - - t.Run("list property force new", func(t *testing.T) { - resMap := map[string]*schema.Resource{ - "prov_test": propertyForceNewResource(schema.TypeList), - "prov_aux": auxResource(schema.TypeList), - } - - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) - runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { - runTest(t, program2, bridgedProvider, expectedOutput) - } - - t.Run("unknown plain property", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - ~ [0]: { - ~ prop: "value" => output - } - ] -`)) - }) - - t.Run("unknown object", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - prop: "value" - } - + [0]: output - ] -`)) - }) - - t.Run("unknown collection", func(t *testing.T) { - program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - tests: [ - - [0]: { - - prop: "value" - } - ] - + tests: output -`)) - }) - }) - - t.Run("set force new", func(t *testing.T) { - resMap := map[string]*schema.Resource{ - "prov_test": collectionForceNewResource(schema.TypeSet), - "prov_aux": auxResource(schema.TypeSet), - } - - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) - runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { - runTest(t, program2, bridgedProvider, expectedOutput) - } - - t.Run("unknown plain property", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - ~ prov:index/test:Test: (update) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - ~ [0]: { - ~ prop: "value" => output - } - ] -`)) - }) - - t.Run("unknown object", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - prop: "value" - } - + [0]: output - ] -`)) - }) - - t.Run("unknown collection", func(t *testing.T) { - program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - tests: [ - - [0]: { - - prop: "value" - } - ] - + tests: output -`)) - }) - }) - - t.Run("set property force new", func(t *testing.T) { - resMap := map[string]*schema.Resource{ - "prov_test": propertyForceNewResource(schema.TypeSet), - "prov_aux": auxResource(schema.TypeSet), - } - - tfp := &schema.Provider{ResourcesMap: resMap} - bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) - runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { - runTest(t, program2, bridgedProvider, expectedOutput) - } - - t.Run("unknown plain property", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - ~ [0]: { - ~ prop: "value" => output - } - ] -`)) - }) - - t.Run("unknown object", func(t *testing.T) { - program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - ~ tests: [ - - [0]: { - - prop: "value" - } - + [0]: output - ] -`)) - }) + type testOutput struct { + initialValue map[string]string + changeValue map[string]string + tfOut string + pulumiOut string + detailedDiff map[string]any + } - t.Run("unknown collection", func(t *testing.T) { - program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") - runTest(t, program2, autogold.Expect(` - + prov:index/aux:Aux: (create) - [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] - +-prov:index/test:Test: (replace) - [id=newid] - [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] - - tests: [ - - [0]: { - - prop: "value" - } - ] - + tests: output -`)) + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + t.Parallel() + diff := crosstests.Diff(t, &res, ctyVal(scenario.initialValue), ctyVal(scenario.changeValue)) + autogold.ExpectFile(t, testOutput{ + initialValue: scenario.initialValue, + changeValue: scenario.changeValue, + tfOut: diff.TFOut, + pulumiOut: diff.PulumiOut, + detailedDiff: diff.PulumiDiff.DetailedDiff, + }) }) - }) + } } diff --git a/pkg/tests/detailed_diff_unknown_test.go b/pkg/tests/detailed_diff_unknown_test.go new file mode 100644 index 000000000..b0faa9d9f --- /dev/null +++ b/pkg/tests/detailed_diff_unknown_test.go @@ -0,0 +1,1058 @@ +package tests + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hexops/autogold/v2" + "github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview" + "github.com/stretchr/testify/require" + + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/pulcheck" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" +) + +func TestUnknownHandling(t *testing.T) { + t.Parallel() + resMap := map[string]*schema.Resource{ + "prov_test": { + Schema: map[string]*schema.Schema{ + "test": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + "prov_aux": { + Schema: map[string]*schema.Schema{ + "aux": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + }, + CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { + d.SetId("aux") + err := d.Set("aux", "aux") + require.NoError(t, err) + return nil + }, + }, + } + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp) + program := ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + mainRes: + type: prov:index:Test + properties: + test: ${auxRes.aux} +outputs: + testOut: ${mainRes.test} +` + pt := pulcheck.PulCheck(t, bridgedProvider, program) + res := pt.Preview(t, optpreview.Diff()) + // Test that the test property is unknown at preview time + require.Contains(t, res.StdOut, "test : output") + resUp := pt.Up(t) + // assert that the property gets resolved + require.Equal(t, "aux", resUp.Outputs["testOut"].Value) +} + +func trimDiff(t *testing.T, diff string) string { + urnLine := " [urn=urn:pulumi:test::test::pulumi:pulumi:Stack::test-test]" + resourcesSummaryLine := "Resources:\n" + require.Contains(t, diff, urnLine) + require.Contains(t, diff, resourcesSummaryLine) + + // trim the diff to only include the contents after the URN line and before the summary + urnIndex := strings.Index(diff, urnLine) + resourcesSummaryIndex := strings.Index(diff, resourcesSummaryLine) + return diff[urnIndex+len(urnLine) : resourcesSummaryIndex] +} + +func TestUnknownBlocks(t *testing.T) { + t.Parallel() + resMap := map[string]*schema.Resource{ + "prov_test": { + Schema: map[string]*schema.Schema{ + "test": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test_prop": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + "prov_nested_test": { + Schema: map[string]*schema.Schema{ + "test": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_prop": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test_prop": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "prov_aux": { + Schema: map[string]*schema.Schema{ + "aux": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test_prop": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + }, + }, + }, + "nested_aux": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "nested_prop": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test_prop": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { + d.SetId("aux") + if d.Get("aux") == nil { + err := d.Set("aux", []map[string]interface{}{{"test_prop": "aux"}}) + require.NoError(t, err) + } + if d.Get("nested_aux") == nil { + err := d.Set("nested_aux", []map[string]interface{}{ + { + "nested_prop": []map[string]interface{}{ + {"test_prop": []string{"aux"}}, + }, + }, + }) + require.NoError(t, err) + } + return nil + }, + }, + } + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp) + + provTestKnownProgram := ` +name: test +runtime: yaml +resources: + mainRes: + type: prov:index:Test + properties: + tests: + - testProp: "known_val" +` + nestedProvTestKnownProgram := ` +name: test +runtime: yaml +resources: + mainRes: + type: prov:index:NestedTest + properties: + tests: + - nestedProps: + - testProps: + - "known_val" +` + + for _, tc := range []struct { + name string + program string + initialKnownProgram string + expectedInitial autogold.Value + expectedUpdate autogold.Value + }{ + { + "list of objects", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:Test + properties: + tests: ${auxRes.auxes} +`, + provTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/test:Test: (create) + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + tests : output +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/test:Test: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + - tests: [ + - [0]: { + - testProp: "known_val" + } + ] + + tests: output +`), + }, + { + "unknown object", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:Test + properties: + tests: + - ${auxRes.auxes[0]} +`, + provTestKnownProgram, + + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/test:Test: (create) + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + tests : [ + [0]: output + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/test:Test: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - testProp: "known_val" + } + + [0]: output + ] +`), + }, + { + "unknown object with others", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:Test + properties: + tests: + - ${auxRes.auxes[0]} + - {"testProp": "val"} +`, + provTestKnownProgram, + + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/test:Test: (create) + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + tests : [ + [0]: output + [1]: { + testProp : "val" + } + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/test:Test: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - testProp: "known_val" + } + + [0]: output + + [1]: { + + testProp : "val" + } + ] +`), + }, + { + "unknown nested", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: ${auxRes.nestedAuxes} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : output +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + - tests: [ + - [0]: { + - nestedProps: [ + - [0]: { + - testProps: [ + - [0]: "known_val" + ] + } + ] + } + ] + + tests: output +`), + }, + { + "unknown nested level 1", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: + - ${auxRes.nestedAuxes[0]} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : [ + [0]: output + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + ~ tests: [ + - [0]: { + - nestedProps: [ + - [0]: { + - testProps: [ + - [0]: "known_val" + ] + } + ] + } + + [0]: output + ] +`), + }, + { + "unknown nested level 2", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: + - nestedProps: ${auxRes.nestedAuxes[0].nestedProps} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : [ + [0]: { + nestedProps: output + } + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + ~ tests: [ + ~ [0]: { + - nestedProps: [ + - [0]: { + - testProps: [ + - [0]: "known_val" + ] + } + ] + + nestedProps: output + } + ] +`), + }, + { + "unknown nested level 3", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: + - nestedProps: + - ${auxRes.nestedAuxes[0].nestedProps[0]} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : [ + [0]: { + nestedProps: [ + [0]: output + ] + } + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + ~ tests: [ + ~ [0]: { + ~ nestedProps: [ + - [0]: { + - testProps: [ + - [0]: "known_val" + ] + } + + [0]: output + ] + } + ] +`), + }, + { + "unknown nested level 4", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: + - nestedProps: + - testProps: ${auxRes.nestedAuxes[0].nestedProps[0].testProps} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : [ + [0]: { + nestedProps: [ + [0]: { + testProps : output + } + ] + } + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + ~ tests: [ + ~ [0]: { + ~ nestedProps: [ + ~ [0]: { + - testProps: [ + - [0]: "known_val" + ] + + testProps: output + } + ] + } + ] +`), + }, + { + "unknown nested level 5", + ` +name: test +runtime: yaml +resources: + auxRes: + type: prov:index:Aux + properties: + auxes: %s + nestedAuxes: %s + mainRes: + type: prov:index:NestedTest + properties: + tests: + - nestedProps: + - testProps: + - ${auxRes.nestedAuxes[0].nestedProps[0].testProps[0]} +`, + nestedProvTestKnownProgram, + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + + prov:index/nestedTest:NestedTest: (create) + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + tests : [ + [0]: { + nestedProps: [ + [0]: { + testProps : [ + [0]: output + ] + } + ] + } + ] +`), + autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/nestedTest:NestedTest: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/nestedTest:NestedTest::mainRes] + ~ tests: [ + ~ [0]: { + ~ nestedProps: [ + ~ [0]: { + ~ testProps: [ + ~ [0]: "known_val" => output + ] + } + ] + } + ] +`), + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + computedProgram := fmt.Sprintf(tc.program, "null", "null") + + t.Run("initial preview", func(t *testing.T) { + pt := pulcheck.PulCheck(t, bridgedProvider, computedProgram) + res := pt.Preview(t, optpreview.Diff()) + t.Log(res.StdOut) + + tc.expectedInitial.Equal(t, trimDiff(t, res.StdOut)) + }) + + t.Run("update preview", func(t *testing.T) { + t.Parallel() + t.Skipf("Skipping this test as it this case is not handled by the TF plugin sdk") + // The TF plugin SDK does not handle removing an input for a computed value, even if the provider implements it. + // The plugin SDK always fills an empty Computed property with the value from the state. + // Diff in these cases always returns no diff and the old state value is used. + nonComputedProgram := fmt.Sprintf(tc.program, "[{testProp: \"val1\"}]", "[{nestedProps: [{testProps: [\"val1\"]}]}]") + pt := pulcheck.PulCheck(t, bridgedProvider, nonComputedProgram) + pt.Up(t) + + pulumiYamlPath := filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml") + + err := os.WriteFile(pulumiYamlPath, []byte(computedProgram), 0o600) + require.NoError(t, err) + + res := pt.Preview(t, optpreview.Diff()) + t.Log(res.StdOut) + tc.expectedUpdate.Equal(t, trimDiff(t, res.StdOut)) + }) + + t.Run("update preview with computed", func(t *testing.T) { + t.Parallel() + pt := pulcheck.PulCheck(t, bridgedProvider, tc.initialKnownProgram) + pt.Up(t) + + pulumiYamlPath := filepath.Join(pt.CurrentStack().Workspace().WorkDir(), "Pulumi.yaml") + + err := os.WriteFile(pulumiYamlPath, []byte(computedProgram), 0o600) + require.NoError(t, err) + + res := pt.Preview(t, optpreview.Diff()) + t.Log(res.StdOut) + tc.expectedUpdate.Equal(t, trimDiff(t, res.StdOut)) + }) + }) + } +} + +func TestUnknownCollectionForceNewDetailedDiff(t *testing.T) { + t.Parallel() + collectionForceNewResource := func(typ schema.ValueType) *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test": { + Type: typ, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prop": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } + } + + propertyForceNewResource := func(typ schema.ValueType) *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "test": { + Type: typ, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prop": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + }, + } + } + + auxResource := func(typ schema.ValueType) *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aux": { + Type: typ, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "prop": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { + d.SetId("aux") + err := d.Set("aux", []map[string]interface{}{{"prop": "aux"}}) + require.NoError(t, err) + return nil + }, + } + } + + initialProgram := ` + name: test + runtime: yaml + resources: + mainRes: + type: prov:index:Test + properties: + tests: [{prop: 'value'}] +` + + program := ` + name: test + runtime: yaml + resources: + auxRes: + type: prov:index:Aux + mainRes: + type: prov:index:Test + properties: + tests: %s +` + + runTest := func(t *testing.T, program2 string, bridgedProvider info.Provider, expectedOutput autogold.Value) { + pt := pulcheck.PulCheck(t, bridgedProvider, initialProgram) + pt.Up(t) + pt.WritePulumiYaml(t, program2) + + res := pt.Preview(t, optpreview.Diff()) + + expectedOutput.Equal(t, trimDiff(t, res.StdOut)) + } + + t.Run("list force new", func(t *testing.T) { + resMap := map[string]*schema.Resource{ + "prov_test": collectionForceNewResource(schema.TypeList), + "prov_aux": auxResource(schema.TypeList), + } + + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) + runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { + runTest(t, program2, bridgedProvider, expectedOutput) + } + + t.Run("unknown plain property", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/test:Test: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + ~ [0]: { + ~ prop: "value" => output + } + ] +`)) + }) + + t.Run("unknown object", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - prop: "value" + } + + [0]: output + ] +`)) + }) + + t.Run("unknown collection", func(t *testing.T) { + program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + - tests: [ + - [0]: { + - prop: "value" + } + ] + + tests: output +`)) + }) + }) + + t.Run("list property force new", func(t *testing.T) { + resMap := map[string]*schema.Resource{ + "prov_test": propertyForceNewResource(schema.TypeList), + "prov_aux": auxResource(schema.TypeList), + } + + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) + runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { + runTest(t, program2, bridgedProvider, expectedOutput) + } + + t.Run("unknown plain property", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + ~ [0]: { + ~ prop: "value" => output + } + ] +`)) + }) + + t.Run("unknown object", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - prop: "value" + } + + [0]: output + ] +`)) + }) + + t.Run("unknown collection", func(t *testing.T) { + program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + - tests: [ + - [0]: { + - prop: "value" + } + ] + + tests: output +`)) + }) + }) + + t.Run("set force new", func(t *testing.T) { + resMap := map[string]*schema.Resource{ + "prov_test": collectionForceNewResource(schema.TypeSet), + "prov_aux": auxResource(schema.TypeSet), + } + + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) + runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { + runTest(t, program2, bridgedProvider, expectedOutput) + } + + t.Run("unknown plain property", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + ~ prov:index/test:Test: (update) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + ~ [0]: { + ~ prop: "value" => output + } + ] +`)) + }) + + t.Run("unknown object", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - prop: "value" + } + + [0]: output + ] +`)) + }) + + t.Run("unknown collection", func(t *testing.T) { + program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + - tests: [ + - [0]: { + - prop: "value" + } + ] + + tests: output +`)) + }) + }) + + t.Run("set property force new", func(t *testing.T) { + resMap := map[string]*schema.Resource{ + "prov_test": propertyForceNewResource(schema.TypeSet), + "prov_aux": auxResource(schema.TypeSet), + } + + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp, pulcheck.EnableAccurateBridgePreviews()) + runTest := func(t *testing.T, program2 string, expectedOutput autogold.Value) { + runTest(t, program2, bridgedProvider, expectedOutput) + } + + t.Run("unknown plain property", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[{prop: \"${auxRes.auxes[0].prop}\"}]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + ~ [0]: { + ~ prop: "value" => output + } + ] +`)) + }) + + t.Run("unknown object", func(t *testing.T) { + program2 := fmt.Sprintf(program, "[\"${auxRes.auxes[0]}\"]") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + ~ tests: [ + - [0]: { + - prop: "value" + } + + [0]: output + ] +`)) + }) + + t.Run("unknown collection", func(t *testing.T) { + program2 := fmt.Sprintf(program, "\"${auxRes.auxes}\"") + runTest(t, program2, autogold.Expect(` + + prov:index/aux:Aux: (create) + [urn=urn:pulumi:test::test::prov:index/aux:Aux::auxRes] + +-prov:index/test:Test: (replace) + [id=newid] + [urn=urn:pulumi:test::test::prov:index/test:Test::mainRes] + - tests: [ + - [0]: { + - prop: "value" + } + ] + + tests: output +`)) + }) + }) +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_empty.golden new file mode 100644 index 000000000..9589fbdf0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + changeValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_non-empty.golden new file mode 100644 index 000000000..f574065dc --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/added_non-empty.golden @@ -0,0 +1,37 @@ +tests.testOutput{ + changeValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + list_attr = [ + + "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + listAttrs: [ + + [0]: "val1" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttrs": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/changed.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/changed.golden new file mode 100644 index 000000000..5337d13ba --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/changed.golden @@ -0,0 +1,38 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val2"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + ~ "val1" -> "val2", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [0]: "val1" => "val2" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttrs[0]": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_back.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_back.golden new file mode 100644 index 000000000..6661382d3 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_back.golden @@ -0,0 +1,45 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + # (1 unchanged element hidden) + "val2", + + "val3", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + + [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttrs[2]": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_front.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_front.golden new file mode 100644 index 000000000..9d3584432 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_front.golden @@ -0,0 +1,51 @@ +tests.testOutput{ + initialValue: &[]string{ + "val2", + "val3", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + + "val1", + "val2", + # (1 unchanged element hidden) + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [0]: "val2" => "val1" + ~ [1]: "val3" => "val2" + + [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listAttrs[0]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[1]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[2]": map[string]interface{}{}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_middle.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_middle.golden new file mode 100644 index 000000000..a4cdb618a --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_added_middle.golden @@ -0,0 +1,49 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val3", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + "val1", + + "val2", + "val3", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [1]: "val3" => "val2" + + [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listAttrs[1]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[2]": map[string]interface{}{}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_end.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_end.golden new file mode 100644 index 000000000..0fad4d531 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_end.golden @@ -0,0 +1,52 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val2", + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + - "val1", + "val2", + - "val3", + + "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [0]: "val1" => "val2" + ~ [1]: "val2" => "val1" + - [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listAttrs[0]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[1]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_front.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_front.golden new file mode 100644 index 000000000..425e86f7b --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_front.golden @@ -0,0 +1,50 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val3", + "val2", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + - "val1", + - "val2", + "val3", + + "val2", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [0]: "val1" => "val3" + - [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listAttrs[0]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_middle.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_middle.golden new file mode 100644 index 000000000..d8373408b --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/list_element_removed_middle.golden @@ -0,0 +1,52 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val3", + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + - "val1", + - "val2", + "val3", + + "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttrs: [ + ~ [0]: "val1" => "val3" + ~ [1]: "val2" => "val1" + - [2]: "val3" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listAttrs[0]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[1]": map[string]interface{}{"kind": "UPDATE"}, + "listAttrs[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_empty.golden new file mode 100644 index 000000000..7bae19762 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + initialValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_non-empty.golden new file mode 100644 index 000000000..91db7793b --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/removed_non-empty.golden @@ -0,0 +1,37 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + - "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + - listAttrs: [ + - [0]: "val1" + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttrs": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_empty.golden new file mode 100644 index 000000000..a53fac34d --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_empty.golden @@ -0,0 +1,11 @@ +tests.testOutput{tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_non-empty.golden new file mode 100644 index 000000000..e250a9f9c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_attribute/unchanged_non-empty.golden @@ -0,0 +1,18 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val1"}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/added_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/added_empty.golden new file mode 100644 index 000000000..9589fbdf0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/added_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + changeValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/added_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/added_non-empty.golden new file mode 100644 index 000000000..303139674 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/added_non-empty.golden @@ -0,0 +1,40 @@ +tests.testOutput{ + changeValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + + list_block { + + prop = "val1" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + listBlocks: [ + + [0]: { + + prop : "val1" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlocks": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/changed.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/changed.golden new file mode 100644 index 000000000..b2bedc1d1 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/changed.golden @@ -0,0 +1,41 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val2"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val1" -> "val2" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [0]: { + ~ prop: "val1" => "val2" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlocks[0].prop": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_back.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_back.golden new file mode 100644 index 000000000..3982f29d0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_back.golden @@ -0,0 +1,48 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + + list_block { + + prop = "val3" + } + + # (2 unchanged blocks hidden) + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + + [2]: { + + prop : "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlocks[2]": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_front.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_front.golden new file mode 100644 index 000000000..ce38ccea8 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_front.golden @@ -0,0 +1,62 @@ +tests.testOutput{ + initialValue: &[]string{ + "val2", + "val3", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val2" -> "val1" + } + ~ list_block { + ~ prop = "val3" -> "val2" + } + + list_block { + + prop = "val3" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [0]: { + ~ prop: "val2" => "val1" + } + ~ [1]: { + ~ prop: "val3" => "val2" + } + + [2]: { + + prop : "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listBlocks[0].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[1].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[2]": map[string]interface{}{}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_middle.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_middle.golden new file mode 100644 index 000000000..708a968c6 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_added_middle.golden @@ -0,0 +1,57 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val3", + }, + changeValue: &[]string{ + "val1", + "val2", + "val3", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val3" -> "val2" + } + + list_block { + + prop = "val3" + } + + # (1 unchanged block hidden) + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [1]: { + ~ prop: "val3" => "val2" + } + + [2]: { + + prop : "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listBlocks[1].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[2]": map[string]interface{}{}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_end.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_end.golden new file mode 100644 index 000000000..7f26041ed --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_end.golden @@ -0,0 +1,62 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val2", + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val1" -> "val2" + } + ~ list_block { + ~ prop = "val2" -> "val1" + } + - list_block { + - prop = "val3" -> null + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [0]: { + ~ prop: "val1" => "val2" + } + ~ [1]: { + ~ prop: "val2" => "val1" + } + - [2]: { + - prop: "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listBlocks[0].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[1].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_front.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_front.golden new file mode 100644 index 000000000..f695583e5 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_front.golden @@ -0,0 +1,57 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val3", + "val2", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val1" -> "val3" + } + - list_block { + - prop = "val3" -> null + } + + # (1 unchanged block hidden) + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [0]: { + ~ prop: "val1" => "val3" + } + - [2]: { + - prop: "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listBlocks[0].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_middle.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_middle.golden new file mode 100644 index 000000000..fad5f4aef --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/list_element_removed_middle.golden @@ -0,0 +1,62 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + "val2", + "val3", + }, + changeValue: &[]string{ + "val3", + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ prop = "val1" -> "val3" + } + ~ list_block { + ~ prop = "val2" -> "val1" + } + - list_block { + - prop = "val3" -> null + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlocks: [ + ~ [0]: { + ~ prop: "val1" => "val3" + } + ~ [1]: { + ~ prop: "val2" => "val1" + } + - [2]: { + - prop: "val3" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "listBlocks[0].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[1].prop": map[string]interface{}{"kind": "UPDATE"}, + "listBlocks[2]": map[string]interface{}{"kind": "DELETE"}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_empty.golden new file mode 100644 index 000000000..7bae19762 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + initialValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_non-empty.golden new file mode 100644 index 000000000..59f844e2b --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/removed_non-empty.golden @@ -0,0 +1,40 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + - list_block { + - prop = "val1" -> null + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + - listBlocks: [ + - [0]: { + - prop: "val1" + } + ] +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlocks": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_empty.golden new file mode 100644 index 000000000..a53fac34d --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_empty.golden @@ -0,0 +1,11 @@ +tests.testOutput{tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`} diff --git a/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_non-empty.golden new file mode 100644 index 000000000..e250a9f9c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/list_block/unchanged_non-empty.golden @@ -0,0 +1,18 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val1"}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_empty.golden new file mode 100644 index 000000000..9589fbdf0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + changeValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_non-empty.golden new file mode 100644 index 000000000..89350bb88 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/added_non-empty.golden @@ -0,0 +1,35 @@ +tests.testOutput{ + changeValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + list_attr = [ + + "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + listAttr: "val1" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttr": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/changed.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/changed.golden new file mode 100644 index 000000000..2e7dd97f3 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/changed.golden @@ -0,0 +1,36 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val2"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + ~ "val1" -> "val2", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listAttr: "val1" => "val2" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttr": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_empty.golden new file mode 100644 index 000000000..7bae19762 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + initialValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_non-empty.golden new file mode 100644 index 000000000..26e9a05e7 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/removed_non-empty.golden @@ -0,0 +1,35 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ list_attr = [ + - "val1", + ] + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + - listAttr: "val1" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listAttr": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_empty.golden new file mode 100644 index 000000000..a53fac34d --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_empty.golden @@ -0,0 +1,11 @@ +tests.testOutput{tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_non-empty.golden new file mode 100644 index 000000000..e250a9f9c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_attribute/unchanged_non-empty.golden @@ -0,0 +1,18 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val1"}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_empty.golden new file mode 100644 index 000000000..9589fbdf0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + changeValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_non-empty.golden new file mode 100644 index 000000000..c81e0e1f0 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/added_non-empty.golden @@ -0,0 +1,38 @@ +tests.testOutput{ + changeValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + + list_block { + + nested_prop = "val1" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + listBlock: { + + nestedProp: "val1" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlock": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/changed.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/changed.golden new file mode 100644 index 000000000..c0d558ea3 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/changed.golden @@ -0,0 +1,39 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val2"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + ~ list_block { + ~ nested_prop = "val1" -> "val2" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ listBlock: { + ~ nestedProp: "val1" => "val2" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlock.nestedProp": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_empty.golden new file mode 100644 index 000000000..7bae19762 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + initialValue: &[]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_non-empty.golden new file mode 100644 index 000000000..fbc5e67f8 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/removed_non-empty.golden @@ -0,0 +1,38 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + - list_block { + - nested_prop = "val1" -> null + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + - listBlock: { + - nestedProp: "val1" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"listBlock": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_empty.golden new file mode 100644 index 000000000..a53fac34d --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_empty.golden @@ -0,0 +1,11 @@ +tests.testOutput{tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`} diff --git a/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_non-empty.golden new file mode 100644 index 000000000..e250a9f9c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffList/max_items_one_block/unchanged_non-empty.golden @@ -0,0 +1,18 @@ +tests.testOutput{ + initialValue: &[]string{ + "val1", + }, + changeValue: &[]string{"val1"}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/added.golden b/pkg/tests/testdata/TestDetailedDiffMap/added.golden new file mode 100644 index 000000000..cc55fbc40 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/added.golden @@ -0,0 +1,36 @@ +tests.testOutput{ + initialValue: map[string]string{}, + changeValue: map[string]string{"key": "val"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + map_prop = { + + "key" = "val" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + mapProp: { + + key: "val" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"mapProp": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/key_changed.golden b/pkg/tests/testdata/TestDetailedDiffMap/key_changed.golden new file mode 100644 index 000000000..2193f1ef6 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/key_changed.golden @@ -0,0 +1,43 @@ +tests.testOutput{ + initialValue: map[string]string{ + "key": "val", + }, + changeValue: map[string]string{"key2": "val"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ map_prop = { + - "key" = "val" -> null + + "key2" = "val" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ mapProp: { + - key : "val" + + key2: "val" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{ + "mapProp.key": map[string]interface{}{"kind": "DELETE"}, + "mapProp.key2": map[string]interface{}{}, + }, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/removed.golden b/pkg/tests/testdata/TestDetailedDiffMap/removed.golden new file mode 100644 index 000000000..34fbfc62c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/removed.golden @@ -0,0 +1,38 @@ +tests.testOutput{ + initialValue: map[string]string{ + "key": "val", + }, + changeValue: map[string]string{}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ map_prop = { + - "key" = "val" -> null + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ mapProp: { + - key: "val" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"mapProp.key": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffMap/unchanged_empty.golden new file mode 100644 index 000000000..342ab2068 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/unchanged_empty.golden @@ -0,0 +1,16 @@ +tests.testOutput{ + initialValue: map[string]string{}, + changeValue: map[string]string{}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffMap/unchanged_non-empty.golden new file mode 100644 index 000000000..95f4be8e3 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/unchanged_non-empty.golden @@ -0,0 +1,18 @@ +tests.testOutput{ + initialValue: map[string]string{ + "key": "val", + }, + changeValue: map[string]string{"key": "val"}, + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +} diff --git a/pkg/tests/testdata/TestDetailedDiffMap/value_changed.golden b/pkg/tests/testdata/TestDetailedDiffMap/value_changed.golden new file mode 100644 index 000000000..3edd45c2c --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffMap/value_changed.golden @@ -0,0 +1,38 @@ +tests.testOutput{ + initialValue: map[string]string{ + "key": "val", + }, + changeValue: map[string]string{"key": "val2"}, + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ map_prop = { + ~ "key" = "val" -> "val2" + } + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ mapProp: { + ~ key: "val" => "val2" + } +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"mapProp.key": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffString/added.golden b/pkg/tests/testdata/TestDetailedDiffString/added.golden new file mode 100644 index 000000000..e1dba4ce3 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffString/added.golden @@ -0,0 +1,30 @@ +tests.testOutput{ + changeValue: valast.Ptr("val1"), tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + + string_prop = "val1" + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + + stringProp: "val1" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"stringProp": map[string]interface{}{}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffString/changed.golden b/pkg/tests/testdata/TestDetailedDiffString/changed.golden new file mode 100644 index 000000000..d32fc8650 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffString/changed.golden @@ -0,0 +1,31 @@ +tests.testOutput{ + initialValue: valast.Ptr("val1"), changeValue: valast.Ptr("val2"), + tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + ~ string_prop = "val1" -> "val2" + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + ~ stringProp: "val1" => "val2" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"stringProp": map[string]interface{}{"kind": "UPDATE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffString/removed.golden b/pkg/tests/testdata/TestDetailedDiffString/removed.golden new file mode 100644 index 000000000..fca1ebb81 --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffString/removed.golden @@ -0,0 +1,30 @@ +tests.testOutput{ + initialValue: valast.Ptr("val1"), tfOut: ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # crossprovider_test_res.example will be updated in-place + ~ resource "crossprovider_test_res" "example" { + id = "newid" + - string_prop = "val1" -> null + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] + ~ crossprovider:index/testRes:TestRes: (update) + [id=newid] + [urn=urn:pulumi:test::project::crossprovider:index/testRes:TestRes::example] + - stringProp: "val1" +Resources: + ~ 1 to update + 1 unchanged +`, + detailedDiff: map[string]interface{}{"stringProp": map[string]interface{}{"kind": "DELETE"}}, +} diff --git a/pkg/tests/testdata/TestDetailedDiffString/unchanged_empty.golden b/pkg/tests/testdata/TestDetailedDiffString/unchanged_empty.golden new file mode 100644 index 000000000..a53fac34d --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffString/unchanged_empty.golden @@ -0,0 +1,11 @@ +tests.testOutput{tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`} diff --git a/pkg/tests/testdata/TestDetailedDiffString/unchanged_non-empty.golden b/pkg/tests/testdata/TestDetailedDiffString/unchanged_non-empty.golden new file mode 100644 index 000000000..25581866f --- /dev/null +++ b/pkg/tests/testdata/TestDetailedDiffString/unchanged_non-empty.golden @@ -0,0 +1,15 @@ +tests.testOutput{ + initialValue: valast.Ptr("val1"), changeValue: valast.Ptr("val1"), + tfOut: ` +No changes. Your infrastructure matches the configuration. + +Terraform has compared your real infrastructure against your configuration +and found no differences, so no changes are needed. +`, + pulumiOut: `Previewing update (test): + pulumi:pulumi:Stack: (same) + [urn=urn:pulumi:test::project::pulumi:pulumi:Stack::project-test] +Resources: + 2 unchanged +`, +}