diff --git a/command/format/diff_test.go b/command/format/diff_test.go new file mode 100644 index 000000000..679cea374 --- /dev/null +++ b/command/format/diff_test.go @@ -0,0 +1,152 @@ +package format + +import ( + "testing" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/plans" + "github.com/mitchellh/colorstring" + "github.com/zclconf/go-cty/cty" +) + +func TestResourceChange(t *testing.T) { + testCases := map[string]struct { + Action plans.Action + Mode addrs.ResourceMode + Before cty.Value + After cty.Value + Schema *configschema.Block + RequiredReplace cty.PathSet + ExpectedOutput string + }{ + "creation": { + Action: plans.Create, + Mode: addrs.ManagedResourceMode, + Before: cty.NullVal(cty.EmptyObject), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + RequiredReplace: cty.NewPathSet(), + ExpectedOutput: ` # test_instance.example will be created + + resource "test_instance" "example" { + + id = (known after apply) + } +`, + }, + "deletion": { + Action: plans.Delete, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + }), + After: cty.NullVal(cty.EmptyObject), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + RequiredReplace: cty.NewPathSet(), + ExpectedOutput: ` # test_instance.example will be destroyed + - resource "test_instance" "example" { + - id = "i-02ae66f368e8518a9" -> null + } +`, + }, + "simple in-place update": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, + }, + RequiredReplace: cty.NewPathSet(), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + } +`, + }, + "simple force-new update": { + Action: plans.DeleteThenCreate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "ami": cty.StringVal("ami-AFTER"), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, + }, + RequiredReplace: cty.NewPathSet(cty.Path{ + cty.GetAttrStep{Name: "ami"}, + }), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ id = "i-02ae66f368e8518a9" -> (known after apply) + } +`, + }, + } + + color := &colorstring.Colorize{Colors: colorstring.DefaultColors, Disable: true} + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + beforeVal := tc.Before + before, err := plans.NewDynamicValue(beforeVal, beforeVal.Type()) + if err != nil { + t.Fatal(err) + } + + afterVal := tc.After + after, err := plans.NewDynamicValue(afterVal, afterVal.Type()) + if err != nil { + t.Fatal(err) + } + + change := &plans.ResourceInstanceChangeSrc{ + Addr: addrs.Resource{ + Mode: tc.Mode, + Type: "test_instance", + Name: "example", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + ProviderAddr: addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance), + ChangeSrc: plans.ChangeSrc{ + Action: tc.Action, + Before: before, + After: after, + }, + RequiredReplace: tc.RequiredReplace, + } + + output := ResourceChange(change, tc.Schema, color) + if output != tc.ExpectedOutput { + t.Fatalf("Unexpected diff.\nExpected:\n%s\nGiven:\n%s\n", tc.ExpectedOutput, output) + } + }) + } +}