github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonplan/values_test.go (about) 1 package jsonplan 2 3 import ( 4 "encoding/json" 5 "reflect" 6 "testing" 7 8 "github.com/hashicorp/terraform/internal/addrs" 9 "github.com/hashicorp/terraform/internal/configs/configschema" 10 "github.com/hashicorp/terraform/internal/plans" 11 "github.com/hashicorp/terraform/internal/terraform" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 func TestMarshalAttributeValues(t *testing.T) { 16 tests := []struct { 17 Attr cty.Value 18 Schema *configschema.Block 19 Want attributeValues 20 }{ 21 { 22 cty.NilVal, 23 &configschema.Block{ 24 Attributes: map[string]*configschema.Attribute{ 25 "foo": { 26 Type: cty.String, 27 Optional: true, 28 }, 29 }, 30 }, 31 nil, 32 }, 33 { 34 cty.NullVal(cty.String), 35 &configschema.Block{ 36 Attributes: map[string]*configschema.Attribute{ 37 "foo": { 38 Type: cty.String, 39 Optional: true, 40 }, 41 }, 42 }, 43 nil, 44 }, 45 { 46 cty.ObjectVal(map[string]cty.Value{ 47 "foo": cty.StringVal("bar"), 48 }), 49 &configschema.Block{ 50 Attributes: map[string]*configschema.Attribute{ 51 "foo": { 52 Type: cty.String, 53 Optional: true, 54 }, 55 }, 56 }, 57 attributeValues{"foo": json.RawMessage(`"bar"`)}, 58 }, 59 { 60 cty.ObjectVal(map[string]cty.Value{ 61 "foo": cty.NullVal(cty.String), 62 }), 63 &configschema.Block{ 64 Attributes: map[string]*configschema.Attribute{ 65 "foo": { 66 Type: cty.String, 67 Optional: true, 68 }, 69 }, 70 }, 71 attributeValues{"foo": json.RawMessage(`null`)}, 72 }, 73 { 74 cty.ObjectVal(map[string]cty.Value{ 75 "bar": cty.MapVal(map[string]cty.Value{ 76 "hello": cty.StringVal("world"), 77 }), 78 "baz": cty.ListVal([]cty.Value{ 79 cty.StringVal("goodnight"), 80 cty.StringVal("moon"), 81 }), 82 }), 83 &configschema.Block{ 84 Attributes: map[string]*configschema.Attribute{ 85 "bar": { 86 Type: cty.Map(cty.String), 87 Required: true, 88 }, 89 "baz": { 90 Type: cty.List(cty.String), 91 Optional: true, 92 }, 93 }, 94 }, 95 attributeValues{ 96 "bar": json.RawMessage(`{"hello":"world"}`), 97 "baz": json.RawMessage(`["goodnight","moon"]`), 98 }, 99 }, 100 } 101 102 for _, test := range tests { 103 got := marshalAttributeValues(test.Attr, test.Schema) 104 eq := reflect.DeepEqual(got, test.Want) 105 if !eq { 106 t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) 107 } 108 } 109 } 110 111 func TestMarshalPlannedOutputs(t *testing.T) { 112 after, _ := plans.NewDynamicValue(cty.StringVal("after"), cty.DynamicPseudoType) 113 114 tests := []struct { 115 Changes *plans.Changes 116 Want map[string]output 117 Err bool 118 }{ 119 { 120 &plans.Changes{}, 121 nil, 122 false, 123 }, 124 { 125 &plans.Changes{ 126 Outputs: []*plans.OutputChangeSrc{ 127 { 128 Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), 129 ChangeSrc: plans.ChangeSrc{ 130 Action: plans.Create, 131 After: after, 132 }, 133 Sensitive: false, 134 }, 135 }, 136 }, 137 map[string]output{ 138 "bar": { 139 Sensitive: false, 140 Type: json.RawMessage(`"string"`), 141 Value: json.RawMessage(`"after"`), 142 }, 143 }, 144 false, 145 }, 146 { // Delete action 147 &plans.Changes{ 148 Outputs: []*plans.OutputChangeSrc{ 149 { 150 Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance), 151 ChangeSrc: plans.ChangeSrc{ 152 Action: plans.Delete, 153 }, 154 Sensitive: false, 155 }, 156 }, 157 }, 158 map[string]output{}, 159 false, 160 }, 161 } 162 163 for _, test := range tests { 164 got, err := marshalPlannedOutputs(test.Changes) 165 if test.Err { 166 if err == nil { 167 t.Fatal("succeeded; want error") 168 } 169 return 170 } else if err != nil { 171 t.Fatalf("unexpected error: %s", err) 172 } 173 174 eq := reflect.DeepEqual(got, test.Want) 175 if !eq { 176 t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) 177 } 178 } 179 } 180 181 func TestMarshalPlanResources(t *testing.T) { 182 tests := map[string]struct { 183 Action plans.Action 184 Before cty.Value 185 After cty.Value 186 Want []resource 187 Err bool 188 }{ 189 "create with unknowns": { 190 Action: plans.Create, 191 Before: cty.NullVal(cty.EmptyObject), 192 After: cty.ObjectVal(map[string]cty.Value{ 193 "woozles": cty.UnknownVal(cty.String), 194 "foozles": cty.UnknownVal(cty.String), 195 }), 196 Want: []resource{{ 197 Address: "test_thing.example", 198 Mode: "managed", 199 Type: "test_thing", 200 Name: "example", 201 Index: addrs.InstanceKey(nil), 202 ProviderName: "registry.terraform.io/hashicorp/test", 203 SchemaVersion: 1, 204 AttributeValues: attributeValues{}, 205 SensitiveValues: json.RawMessage("{}"), 206 }}, 207 Err: false, 208 }, 209 "delete with null and nil": { 210 Action: plans.Delete, 211 Before: cty.NullVal(cty.EmptyObject), 212 After: cty.NilVal, 213 Want: nil, 214 Err: false, 215 }, 216 "delete": { 217 Action: plans.Delete, 218 Before: cty.ObjectVal(map[string]cty.Value{ 219 "woozles": cty.StringVal("foo"), 220 "foozles": cty.StringVal("bar"), 221 }), 222 After: cty.NullVal(cty.Object(map[string]cty.Type{ 223 "woozles": cty.String, 224 "foozles": cty.String, 225 })), 226 Want: nil, 227 Err: false, 228 }, 229 "update without unknowns": { 230 Action: plans.Update, 231 Before: cty.ObjectVal(map[string]cty.Value{ 232 "woozles": cty.StringVal("foo"), 233 "foozles": cty.StringVal("bar"), 234 }), 235 After: cty.ObjectVal(map[string]cty.Value{ 236 "woozles": cty.StringVal("baz"), 237 "foozles": cty.StringVal("bat"), 238 }), 239 Want: []resource{{ 240 Address: "test_thing.example", 241 Mode: "managed", 242 Type: "test_thing", 243 Name: "example", 244 Index: addrs.InstanceKey(nil), 245 ProviderName: "registry.terraform.io/hashicorp/test", 246 SchemaVersion: 1, 247 AttributeValues: attributeValues{ 248 "woozles": json.RawMessage(`"baz"`), 249 "foozles": json.RawMessage(`"bat"`), 250 }, 251 SensitiveValues: json.RawMessage("{}"), 252 }}, 253 Err: false, 254 }, 255 } 256 257 for name, test := range tests { 258 t.Run(name, func(t *testing.T) { 259 before, err := plans.NewDynamicValue(test.Before, test.Before.Type()) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 after, err := plans.NewDynamicValue(test.After, test.After.Type()) 265 if err != nil { 266 t.Fatal(err) 267 } 268 testChange := &plans.Changes{ 269 Resources: []*plans.ResourceInstanceChangeSrc{ 270 { 271 Addr: addrs.Resource{ 272 Mode: addrs.ManagedResourceMode, 273 Type: "test_thing", 274 Name: "example", 275 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 276 ProviderAddr: addrs.AbsProviderConfig{ 277 Provider: addrs.NewDefaultProvider("test"), 278 Module: addrs.RootModule, 279 }, 280 ChangeSrc: plans.ChangeSrc{ 281 Action: test.Action, 282 Before: before, 283 After: after, 284 }, 285 }, 286 }, 287 } 288 289 ris := testResourceAddrs() 290 291 got, err := marshalPlanResources(testChange, ris, testSchemas()) 292 if test.Err { 293 if err == nil { 294 t.Fatal("succeeded; want error") 295 } 296 return 297 } else if err != nil { 298 t.Fatalf("unexpected error: %s", err) 299 } 300 301 eq := reflect.DeepEqual(got, test.Want) 302 if !eq { 303 t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) 304 } 305 }) 306 } 307 } 308 309 func TestMarshalPlanValuesNoopDeposed(t *testing.T) { 310 dynamicNull, err := plans.NewDynamicValue(cty.NullVal(cty.DynamicPseudoType), cty.DynamicPseudoType) 311 if err != nil { 312 t.Fatal(err) 313 } 314 testChange := &plans.Changes{ 315 Resources: []*plans.ResourceInstanceChangeSrc{ 316 { 317 Addr: addrs.Resource{ 318 Mode: addrs.ManagedResourceMode, 319 Type: "test_thing", 320 Name: "example", 321 }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), 322 DeposedKey: "12345678", 323 ProviderAddr: addrs.AbsProviderConfig{ 324 Provider: addrs.NewDefaultProvider("test"), 325 Module: addrs.RootModule, 326 }, 327 ChangeSrc: plans.ChangeSrc{ 328 Action: plans.NoOp, 329 Before: dynamicNull, 330 After: dynamicNull, 331 }, 332 }, 333 }, 334 } 335 336 _, err = marshalPlannedValues(testChange, testSchemas()) 337 if err != nil { 338 t.Fatal(err) 339 } 340 } 341 342 func testSchemas() *terraform.Schemas { 343 return &terraform.Schemas{ 344 Providers: map[addrs.Provider]*terraform.ProviderSchema{ 345 addrs.NewDefaultProvider("test"): &terraform.ProviderSchema{ 346 ResourceTypes: map[string]*configschema.Block{ 347 "test_thing": { 348 Attributes: map[string]*configschema.Attribute{ 349 "woozles": {Type: cty.String, Optional: true, Computed: true}, 350 "foozles": {Type: cty.String, Optional: true}, 351 }, 352 }, 353 }, 354 ResourceTypeSchemaVersions: map[string]uint64{ 355 "test_thing": 1, 356 }, 357 }, 358 }, 359 } 360 } 361 362 func testResourceAddrs() []addrs.AbsResourceInstance { 363 return []addrs.AbsResourceInstance{ 364 mustAddr("test_thing.example"), 365 } 366 } 367 368 func mustAddr(str string) addrs.AbsResourceInstance { 369 addr, diags := addrs.ParseAbsResourceInstanceStr(str) 370 if diags.HasErrors() { 371 panic(diags.Err()) 372 } 373 return addr 374 }