From f94f380943dc859d976741f7e2881006f0a30e54 Mon Sep 17 00:00:00 2001 From: VenelinMartinov Date: Fri, 13 Dec 2024 17:41:06 +0000 Subject: [PATCH] Refactor SDKv2 non-set detailed diff tests (#2725) This is a refactors the rest of the SDKv2 detailed diff tests. Instead of recording them inline, we now run them as cross-tests and record them in separate files along with the name of the test and the input values. This mimics what we did for the PF detailed diff tests in https://github.com/pulumi/pulumi-terraform-bridge/pull/2592 I've gone through the recordings and believe all of them are correct. --- 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 +`, +}