github.com/opentofu/opentofu@v1.7.1/internal/plans/changes_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package plans 7 8 import ( 9 "fmt" 10 "testing" 11 12 "github.com/opentofu/opentofu/internal/addrs" 13 "github.com/opentofu/opentofu/internal/lang/marks" 14 "github.com/zclconf/go-cty/cty" 15 ) 16 17 func TestChangesEmpty(t *testing.T) { 18 testCases := map[string]struct { 19 changes *Changes 20 want bool 21 }{ 22 "no changes": { 23 &Changes{}, 24 true, 25 }, 26 "resource change": { 27 &Changes{ 28 Resources: []*ResourceInstanceChangeSrc{ 29 { 30 Addr: addrs.Resource{ 31 Mode: addrs.ManagedResourceMode, 32 Type: "test_thing", 33 Name: "woot", 34 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 35 PrevRunAddr: addrs.Resource{ 36 Mode: addrs.ManagedResourceMode, 37 Type: "test_thing", 38 Name: "woot", 39 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 40 ChangeSrc: ChangeSrc{ 41 Action: Update, 42 }, 43 }, 44 }, 45 }, 46 false, 47 }, 48 "resource change with no-op action": { 49 &Changes{ 50 Resources: []*ResourceInstanceChangeSrc{ 51 { 52 Addr: addrs.Resource{ 53 Mode: addrs.ManagedResourceMode, 54 Type: "test_thing", 55 Name: "woot", 56 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 57 PrevRunAddr: addrs.Resource{ 58 Mode: addrs.ManagedResourceMode, 59 Type: "test_thing", 60 Name: "woot", 61 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 62 ChangeSrc: ChangeSrc{ 63 Action: NoOp, 64 }, 65 }, 66 }, 67 }, 68 true, 69 }, 70 "resource moved with no-op change": { 71 &Changes{ 72 Resources: []*ResourceInstanceChangeSrc{ 73 { 74 Addr: addrs.Resource{ 75 Mode: addrs.ManagedResourceMode, 76 Type: "test_thing", 77 Name: "woot", 78 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 79 PrevRunAddr: addrs.Resource{ 80 Mode: addrs.ManagedResourceMode, 81 Type: "test_thing", 82 Name: "toot", 83 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 84 ChangeSrc: ChangeSrc{ 85 Action: NoOp, 86 }, 87 }, 88 }, 89 }, 90 false, 91 }, 92 "output change": { 93 &Changes{ 94 Outputs: []*OutputChangeSrc{ 95 { 96 Addr: addrs.OutputValue{ 97 Name: "result", 98 }.Absolute(addrs.RootModuleInstance), 99 ChangeSrc: ChangeSrc{ 100 Action: Update, 101 }, 102 }, 103 }, 104 }, 105 false, 106 }, 107 "output change no-op": { 108 &Changes{ 109 Outputs: []*OutputChangeSrc{ 110 { 111 Addr: addrs.OutputValue{ 112 Name: "result", 113 }.Absolute(addrs.RootModuleInstance), 114 ChangeSrc: ChangeSrc{ 115 Action: NoOp, 116 }, 117 }, 118 }, 119 }, 120 true, 121 }, 122 } 123 124 for name, tc := range testCases { 125 t.Run(name, func(t *testing.T) { 126 if got, want := tc.changes.Empty(), tc.want; got != want { 127 t.Fatalf("unexpected result: got %v, want %v", got, want) 128 } 129 }) 130 } 131 } 132 133 func TestChangeEncodeSensitive(t *testing.T) { 134 testVals := []cty.Value{ 135 cty.ObjectVal(map[string]cty.Value{ 136 "ding": cty.StringVal("dong").Mark(marks.Sensitive), 137 }), 138 cty.StringVal("bleep").Mark("bloop"), 139 cty.ListVal([]cty.Value{cty.UnknownVal(cty.String).Mark("sup?")}), 140 } 141 142 for _, v := range testVals { 143 t.Run(fmt.Sprintf("%#v", v), func(t *testing.T) { 144 change := Change{ 145 Before: cty.NullVal(v.Type()), 146 After: v, 147 } 148 149 encoded, err := change.Encode(v.Type()) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 decoded, err := encoded.Decode(v.Type()) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 if !v.RawEquals(decoded.After) { 160 t.Fatalf("%#v != %#v\n", decoded.After, v) 161 } 162 }) 163 } 164 }