github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plugin/convert/schema_test.go (about) 1 package convert 2 3 import ( 4 "testing" 5 6 "github.com/google/go-cmp/cmp" 7 "github.com/google/go-cmp/cmp/cmpopts" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 9 proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" 10 "github.com/zclconf/go-cty/cty" 11 ) 12 13 var ( 14 equateEmpty = cmpopts.EquateEmpty() 15 typeComparer = cmp.Comparer(cty.Type.Equals) 16 valueComparer = cmp.Comparer(cty.Value.RawEquals) 17 ) 18 19 // Test that we can convert configschema to protobuf types and back again. 20 func TestConvertSchemaBlocks(t *testing.T) { 21 tests := map[string]struct { 22 Block *proto.Schema_Block 23 Want *configschema.Block 24 }{ 25 "attributes": { 26 &proto.Schema_Block{ 27 Attributes: []*proto.Schema_Attribute{ 28 { 29 Name: "computed", 30 Type: []byte(`["list","bool"]`), 31 Computed: true, 32 }, 33 { 34 Name: "optional", 35 Type: []byte(`"string"`), 36 Optional: true, 37 }, 38 { 39 Name: "optional_computed", 40 Type: []byte(`["map","bool"]`), 41 Optional: true, 42 Computed: true, 43 }, 44 { 45 Name: "required", 46 Type: []byte(`"number"`), 47 Required: true, 48 }, 49 }, 50 }, 51 &configschema.Block{ 52 Attributes: map[string]*configschema.Attribute{ 53 "computed": { 54 Type: cty.List(cty.Bool), 55 Computed: true, 56 }, 57 "optional": { 58 Type: cty.String, 59 Optional: true, 60 }, 61 "optional_computed": { 62 Type: cty.Map(cty.Bool), 63 Optional: true, 64 Computed: true, 65 }, 66 "required": { 67 Type: cty.Number, 68 Required: true, 69 }, 70 }, 71 }, 72 }, 73 "blocks": { 74 &proto.Schema_Block{ 75 BlockTypes: []*proto.Schema_NestedBlock{ 76 { 77 TypeName: "list", 78 Nesting: proto.Schema_NestedBlock_LIST, 79 Block: &proto.Schema_Block{}, 80 }, 81 { 82 TypeName: "map", 83 Nesting: proto.Schema_NestedBlock_MAP, 84 Block: &proto.Schema_Block{}, 85 }, 86 { 87 TypeName: "set", 88 Nesting: proto.Schema_NestedBlock_SET, 89 Block: &proto.Schema_Block{}, 90 }, 91 { 92 TypeName: "single", 93 Nesting: proto.Schema_NestedBlock_SINGLE, 94 Block: &proto.Schema_Block{ 95 Attributes: []*proto.Schema_Attribute{ 96 { 97 Name: "foo", 98 Type: []byte(`"dynamic"`), 99 Required: true, 100 }, 101 }, 102 }, 103 }, 104 }, 105 }, 106 &configschema.Block{ 107 BlockTypes: map[string]*configschema.NestedBlock{ 108 "list": { 109 Nesting: configschema.NestingList, 110 }, 111 "map": { 112 Nesting: configschema.NestingMap, 113 }, 114 "set": { 115 Nesting: configschema.NestingSet, 116 }, 117 "single": { 118 Nesting: configschema.NestingSingle, 119 Block: configschema.Block{ 120 Attributes: map[string]*configschema.Attribute{ 121 "foo": { 122 Type: cty.DynamicPseudoType, 123 Required: true, 124 }, 125 }, 126 }, 127 }, 128 }, 129 }, 130 }, 131 "deep block nesting": { 132 &proto.Schema_Block{ 133 BlockTypes: []*proto.Schema_NestedBlock{ 134 { 135 TypeName: "single", 136 Nesting: proto.Schema_NestedBlock_SINGLE, 137 Block: &proto.Schema_Block{ 138 BlockTypes: []*proto.Schema_NestedBlock{ 139 { 140 TypeName: "list", 141 Nesting: proto.Schema_NestedBlock_LIST, 142 Block: &proto.Schema_Block{ 143 BlockTypes: []*proto.Schema_NestedBlock{ 144 { 145 TypeName: "set", 146 Nesting: proto.Schema_NestedBlock_SET, 147 Block: &proto.Schema_Block{}, 148 }, 149 }, 150 }, 151 }, 152 }, 153 }, 154 }, 155 }, 156 }, 157 &configschema.Block{ 158 BlockTypes: map[string]*configschema.NestedBlock{ 159 "single": { 160 Nesting: configschema.NestingSingle, 161 Block: configschema.Block{ 162 BlockTypes: map[string]*configschema.NestedBlock{ 163 "list": { 164 Nesting: configschema.NestingList, 165 Block: configschema.Block{ 166 BlockTypes: map[string]*configschema.NestedBlock{ 167 "set": { 168 Nesting: configschema.NestingSet, 169 }, 170 }, 171 }, 172 }, 173 }, 174 }, 175 }, 176 }, 177 }, 178 }, 179 } 180 181 for name, tc := range tests { 182 t.Run(name, func(t *testing.T) { 183 converted := ProtoToConfigSchema(tc.Block) 184 if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) { 185 t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty)) 186 } 187 }) 188 } 189 } 190 191 // Test that we can convert configschema to protobuf types and back again. 192 func TestConvertProtoSchemaBlocks(t *testing.T) { 193 tests := map[string]struct { 194 Want *proto.Schema_Block 195 Block *configschema.Block 196 }{ 197 "attributes": { 198 &proto.Schema_Block{ 199 Attributes: []*proto.Schema_Attribute{ 200 { 201 Name: "computed", 202 Type: []byte(`["list","bool"]`), 203 Computed: true, 204 }, 205 { 206 Name: "optional", 207 Type: []byte(`"string"`), 208 Optional: true, 209 }, 210 { 211 Name: "optional_computed", 212 Type: []byte(`["map","bool"]`), 213 Optional: true, 214 Computed: true, 215 }, 216 { 217 Name: "required", 218 Type: []byte(`"number"`), 219 Required: true, 220 }, 221 }, 222 }, 223 &configschema.Block{ 224 Attributes: map[string]*configschema.Attribute{ 225 "computed": { 226 Type: cty.List(cty.Bool), 227 Computed: true, 228 }, 229 "optional": { 230 Type: cty.String, 231 Optional: true, 232 }, 233 "optional_computed": { 234 Type: cty.Map(cty.Bool), 235 Optional: true, 236 Computed: true, 237 }, 238 "required": { 239 Type: cty.Number, 240 Required: true, 241 }, 242 }, 243 }, 244 }, 245 "blocks": { 246 &proto.Schema_Block{ 247 BlockTypes: []*proto.Schema_NestedBlock{ 248 { 249 TypeName: "list", 250 Nesting: proto.Schema_NestedBlock_LIST, 251 Block: &proto.Schema_Block{}, 252 }, 253 { 254 TypeName: "map", 255 Nesting: proto.Schema_NestedBlock_MAP, 256 Block: &proto.Schema_Block{}, 257 }, 258 { 259 TypeName: "set", 260 Nesting: proto.Schema_NestedBlock_SET, 261 Block: &proto.Schema_Block{}, 262 }, 263 { 264 TypeName: "single", 265 Nesting: proto.Schema_NestedBlock_SINGLE, 266 Block: &proto.Schema_Block{ 267 Attributes: []*proto.Schema_Attribute{ 268 { 269 Name: "foo", 270 Type: []byte(`"dynamic"`), 271 Required: true, 272 }, 273 }, 274 }, 275 }, 276 }, 277 }, 278 &configschema.Block{ 279 BlockTypes: map[string]*configschema.NestedBlock{ 280 "list": { 281 Nesting: configschema.NestingList, 282 }, 283 "map": { 284 Nesting: configschema.NestingMap, 285 }, 286 "set": { 287 Nesting: configschema.NestingSet, 288 }, 289 "single": { 290 Nesting: configschema.NestingSingle, 291 Block: configschema.Block{ 292 Attributes: map[string]*configschema.Attribute{ 293 "foo": { 294 Type: cty.DynamicPseudoType, 295 Required: true, 296 }, 297 }, 298 }, 299 }, 300 }, 301 }, 302 }, 303 "deep block nesting": { 304 &proto.Schema_Block{ 305 BlockTypes: []*proto.Schema_NestedBlock{ 306 { 307 TypeName: "single", 308 Nesting: proto.Schema_NestedBlock_SINGLE, 309 Block: &proto.Schema_Block{ 310 BlockTypes: []*proto.Schema_NestedBlock{ 311 { 312 TypeName: "list", 313 Nesting: proto.Schema_NestedBlock_LIST, 314 Block: &proto.Schema_Block{ 315 BlockTypes: []*proto.Schema_NestedBlock{ 316 { 317 TypeName: "set", 318 Nesting: proto.Schema_NestedBlock_SET, 319 Block: &proto.Schema_Block{}, 320 }, 321 }, 322 }, 323 }, 324 }, 325 }, 326 }, 327 }, 328 }, 329 &configschema.Block{ 330 BlockTypes: map[string]*configschema.NestedBlock{ 331 "single": { 332 Nesting: configschema.NestingSingle, 333 Block: configschema.Block{ 334 BlockTypes: map[string]*configschema.NestedBlock{ 335 "list": { 336 Nesting: configschema.NestingList, 337 Block: configschema.Block{ 338 BlockTypes: map[string]*configschema.NestedBlock{ 339 "set": { 340 Nesting: configschema.NestingSet, 341 }, 342 }, 343 }, 344 }, 345 }, 346 }, 347 }, 348 }, 349 }, 350 }, 351 } 352 353 for name, tc := range tests { 354 t.Run(name, func(t *testing.T) { 355 converted := ConfigSchemaToProto(tc.Block) 356 if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty) { 357 t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty)) 358 } 359 }) 360 } 361 }