github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/helper/plugin/unknown_test.go (about)

     1  package plugin
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
     7  	"github.com/zclconf/go-cty/cty"
     8  )
     9  
    10  func TestSetUnknowns(t *testing.T) {
    11  	for n, tc := range map[string]struct {
    12  		Schema   *configschema.Block
    13  		Val      cty.Value
    14  		Expected cty.Value
    15  	}{
    16  		"empty": {
    17  			&configschema.Block{},
    18  			cty.EmptyObjectVal,
    19  			cty.EmptyObjectVal,
    20  		},
    21  		"no prior": {
    22  			&configschema.Block{
    23  				Attributes: map[string]*configschema.Attribute{
    24  					"foo": {
    25  						Type:     cty.String,
    26  						Optional: true,
    27  					},
    28  					"bar": {
    29  						Type:     cty.String,
    30  						Computed: true,
    31  					},
    32  				},
    33  				BlockTypes: map[string]*configschema.NestedBlock{
    34  					"baz": {
    35  						Nesting: configschema.NestingSingle,
    36  						Block: configschema.Block{
    37  							Attributes: map[string]*configschema.Attribute{
    38  								"boz": {
    39  									Type:     cty.String,
    40  									Optional: true,
    41  									Computed: true,
    42  								},
    43  								"biz": {
    44  									Type:     cty.String,
    45  									Optional: true,
    46  									Computed: true,
    47  								},
    48  							},
    49  						},
    50  					},
    51  				},
    52  			},
    53  			cty.NullVal(cty.Object(map[string]cty.Type{
    54  				"foo": cty.String,
    55  				"bar": cty.String,
    56  				"baz": cty.Object(map[string]cty.Type{
    57  					"boz": cty.String,
    58  					"biz": cty.String,
    59  				}),
    60  			})),
    61  			cty.ObjectVal(map[string]cty.Value{
    62  				"foo": cty.NullVal(cty.String),
    63  				"bar": cty.UnknownVal(cty.String),
    64  			}),
    65  		},
    66  		"null stays null": {
    67  			// if the object has no computed attributes, it should stay null
    68  			&configschema.Block{
    69  				Attributes: map[string]*configschema.Attribute{
    70  					"foo": {
    71  						Type: cty.String,
    72  					},
    73  				},
    74  				BlockTypes: map[string]*configschema.NestedBlock{
    75  					"baz": {
    76  						Nesting: configschema.NestingSet,
    77  						Block: configschema.Block{
    78  							Attributes: map[string]*configschema.Attribute{
    79  								"boz": {
    80  									Type:     cty.String,
    81  									Optional: true,
    82  									Computed: true,
    83  								},
    84  							},
    85  						},
    86  					},
    87  				},
    88  			},
    89  			cty.NullVal(cty.Object(map[string]cty.Type{
    90  				"foo": cty.String,
    91  				"baz": cty.Set(cty.Object(map[string]cty.Type{
    92  					"boz": cty.String,
    93  				})),
    94  			})),
    95  			cty.NullVal(cty.Object(map[string]cty.Type{
    96  				"foo": cty.String,
    97  				"baz": cty.Set(cty.Object(map[string]cty.Type{
    98  					"boz": cty.String,
    99  				})),
   100  			})),
   101  		},
   102  		"no prior with set": {
   103  			// the set value should remain null
   104  			&configschema.Block{
   105  				Attributes: map[string]*configschema.Attribute{
   106  					"foo": {
   107  						Type:     cty.String,
   108  						Computed: true,
   109  					},
   110  				},
   111  				BlockTypes: map[string]*configschema.NestedBlock{
   112  					"baz": {
   113  						Nesting: configschema.NestingSet,
   114  						Block: configschema.Block{
   115  							Attributes: map[string]*configschema.Attribute{
   116  								"boz": {
   117  									Type:     cty.String,
   118  									Optional: true,
   119  									Computed: true,
   120  								},
   121  							},
   122  						},
   123  					},
   124  				},
   125  			},
   126  			cty.NullVal(cty.Object(map[string]cty.Type{
   127  				"foo": cty.String,
   128  				"baz": cty.Set(cty.Object(map[string]cty.Type{
   129  					"boz": cty.String,
   130  				})),
   131  			})),
   132  			cty.ObjectVal(map[string]cty.Value{
   133  				"foo": cty.UnknownVal(cty.String),
   134  			}),
   135  		},
   136  		"prior attributes": {
   137  			&configschema.Block{
   138  				Attributes: map[string]*configschema.Attribute{
   139  					"foo": {
   140  						Type:     cty.String,
   141  						Optional: true,
   142  					},
   143  					"bar": {
   144  						Type:     cty.String,
   145  						Computed: true,
   146  					},
   147  					"baz": {
   148  						Type:     cty.String,
   149  						Optional: true,
   150  						Computed: true,
   151  					},
   152  					"boz": {
   153  						Type:     cty.String,
   154  						Optional: true,
   155  						Computed: true,
   156  					},
   157  				},
   158  			},
   159  			cty.ObjectVal(map[string]cty.Value{
   160  				"foo": cty.StringVal("bonjour"),
   161  				"bar": cty.StringVal("petit dejeuner"),
   162  				"baz": cty.StringVal("grande dejeuner"),
   163  			}),
   164  			cty.ObjectVal(map[string]cty.Value{
   165  				"foo": cty.StringVal("bonjour"),
   166  				"bar": cty.StringVal("petit dejeuner"),
   167  				"baz": cty.StringVal("grande dejeuner"),
   168  				"boz": cty.UnknownVal(cty.String),
   169  			}),
   170  		},
   171  		"prior nested single": {
   172  			&configschema.Block{
   173  				BlockTypes: map[string]*configschema.NestedBlock{
   174  					"foo": {
   175  						Nesting: configschema.NestingSingle,
   176  						Block: configschema.Block{
   177  							Attributes: map[string]*configschema.Attribute{
   178  								"bar": {
   179  									Type:     cty.String,
   180  									Optional: true,
   181  									Computed: true,
   182  								},
   183  								"baz": {
   184  									Type:     cty.String,
   185  									Optional: true,
   186  									Computed: true,
   187  								},
   188  							},
   189  						},
   190  					},
   191  				},
   192  			},
   193  			cty.ObjectVal(map[string]cty.Value{
   194  				"foo": cty.ObjectVal(map[string]cty.Value{
   195  					"bar": cty.StringVal("beep"),
   196  				}),
   197  			}),
   198  			cty.ObjectVal(map[string]cty.Value{
   199  				"foo": cty.ObjectVal(map[string]cty.Value{
   200  					"bar": cty.StringVal("beep"),
   201  					"baz": cty.UnknownVal(cty.String),
   202  				}),
   203  			}),
   204  		},
   205  		"prior nested list": {
   206  			&configschema.Block{
   207  				BlockTypes: map[string]*configschema.NestedBlock{
   208  					"foo": {
   209  						Nesting: configschema.NestingList,
   210  						Block: configschema.Block{
   211  							Attributes: map[string]*configschema.Attribute{
   212  								"bar": {
   213  									Type:     cty.String,
   214  									Optional: true,
   215  									Computed: true,
   216  								},
   217  								"baz": {
   218  									Type:     cty.String,
   219  									Optional: true,
   220  									Computed: true,
   221  								},
   222  							},
   223  						},
   224  					},
   225  				},
   226  			},
   227  			cty.ObjectVal(map[string]cty.Value{
   228  				"foo": cty.ListVal([]cty.Value{
   229  					cty.ObjectVal(map[string]cty.Value{
   230  						"bar": cty.StringVal("bap"),
   231  					}),
   232  					cty.ObjectVal(map[string]cty.Value{
   233  						"bar": cty.StringVal("blep"),
   234  					}),
   235  				}),
   236  			}),
   237  			cty.ObjectVal(map[string]cty.Value{
   238  				"foo": cty.ListVal([]cty.Value{
   239  					cty.ObjectVal(map[string]cty.Value{
   240  						"bar": cty.StringVal("bap"),
   241  						"baz": cty.UnknownVal(cty.String),
   242  					}),
   243  					cty.ObjectVal(map[string]cty.Value{
   244  						"bar": cty.StringVal("blep"),
   245  						"baz": cty.UnknownVal(cty.String),
   246  					}),
   247  				}),
   248  			}),
   249  		},
   250  		"prior nested map": {
   251  			&configschema.Block{
   252  				BlockTypes: map[string]*configschema.NestedBlock{
   253  					"foo": {
   254  						Nesting: configschema.NestingMap,
   255  						Block: configschema.Block{
   256  							Attributes: map[string]*configschema.Attribute{
   257  								"bar": {
   258  									Type:     cty.String,
   259  									Optional: true,
   260  									Computed: true,
   261  								},
   262  								"baz": {
   263  									Type:     cty.String,
   264  									Optional: true,
   265  									Computed: true,
   266  								},
   267  							},
   268  						},
   269  					},
   270  				},
   271  			},
   272  			cty.ObjectVal(map[string]cty.Value{
   273  				"foo": cty.MapVal(map[string]cty.Value{
   274  					"a": cty.ObjectVal(map[string]cty.Value{
   275  						"bar": cty.NullVal(cty.String),
   276  						"baz": cty.StringVal("boop"),
   277  					}),
   278  					"b": cty.ObjectVal(map[string]cty.Value{
   279  						"bar": cty.StringVal("blep"),
   280  						"baz": cty.NullVal(cty.String),
   281  					}),
   282  				}),
   283  			}),
   284  			cty.ObjectVal(map[string]cty.Value{
   285  				"foo": cty.MapVal(map[string]cty.Value{
   286  					"a": cty.ObjectVal(map[string]cty.Value{
   287  						"bar": cty.UnknownVal(cty.String),
   288  						"baz": cty.StringVal("boop"),
   289  					}),
   290  					"b": cty.ObjectVal(map[string]cty.Value{
   291  						"bar": cty.StringVal("blep"),
   292  						"baz": cty.UnknownVal(cty.String),
   293  					}),
   294  				}),
   295  			}),
   296  		},
   297  		"prior nested set": {
   298  			&configschema.Block{
   299  				BlockTypes: map[string]*configschema.NestedBlock{
   300  					"foo": {
   301  						Nesting: configschema.NestingSet,
   302  						Block: configschema.Block{
   303  							Attributes: map[string]*configschema.Attribute{
   304  								"bar": {
   305  									Type:     cty.String,
   306  									Optional: true,
   307  								},
   308  								"baz": {
   309  									Type:     cty.String,
   310  									Optional: true,
   311  									Computed: true,
   312  								},
   313  							},
   314  						},
   315  					},
   316  				},
   317  			},
   318  			cty.ObjectVal(map[string]cty.Value{
   319  				"foo": cty.SetVal([]cty.Value{
   320  					cty.ObjectVal(map[string]cty.Value{
   321  						"bar": cty.StringVal("blep"),
   322  						"baz": cty.NullVal(cty.String),
   323  					}),
   324  					cty.ObjectVal(map[string]cty.Value{
   325  						"bar": cty.StringVal("boop"),
   326  						"baz": cty.NullVal(cty.String),
   327  					}),
   328  				}),
   329  			}),
   330  			cty.ObjectVal(map[string]cty.Value{
   331  				"foo": cty.SetVal([]cty.Value{
   332  					cty.ObjectVal(map[string]cty.Value{
   333  						"bar": cty.StringVal("blep"),
   334  						"baz": cty.UnknownVal(cty.String),
   335  					}),
   336  					cty.ObjectVal(map[string]cty.Value{
   337  						"bar": cty.StringVal("boop"),
   338  						"baz": cty.UnknownVal(cty.String),
   339  					}),
   340  				}),
   341  			}),
   342  		},
   343  		"sets differing only by unknown": {
   344  			&configschema.Block{
   345  				BlockTypes: map[string]*configschema.NestedBlock{
   346  					"foo": {
   347  						Nesting: configschema.NestingSet,
   348  						Block: configschema.Block{
   349  							Attributes: map[string]*configschema.Attribute{
   350  								"bar": {
   351  									Type:     cty.String,
   352  									Optional: true,
   353  								},
   354  								"baz": {
   355  									Type:     cty.String,
   356  									Optional: true,
   357  									Computed: true,
   358  								},
   359  							},
   360  						},
   361  					},
   362  				},
   363  			},
   364  			cty.ObjectVal(map[string]cty.Value{
   365  				"foo": cty.SetVal([]cty.Value{
   366  					cty.ObjectVal(map[string]cty.Value{
   367  						"bar": cty.StringVal("boop"),
   368  						"baz": cty.NullVal(cty.String),
   369  					}),
   370  					cty.ObjectVal(map[string]cty.Value{
   371  						"bar": cty.StringVal("boop"),
   372  						"baz": cty.UnknownVal(cty.String),
   373  					}),
   374  				}),
   375  			}),
   376  			cty.ObjectVal(map[string]cty.Value{
   377  				"foo": cty.SetVal([]cty.Value{
   378  					cty.ObjectVal(map[string]cty.Value{
   379  						"bar": cty.StringVal("boop"),
   380  						"baz": cty.UnknownVal(cty.String),
   381  					}),
   382  					cty.ObjectVal(map[string]cty.Value{
   383  						"bar": cty.StringVal("boop"),
   384  						"baz": cty.UnknownVal(cty.String),
   385  					}),
   386  				}),
   387  			}),
   388  		},
   389  		"prior nested list with dynamic": {
   390  			&configschema.Block{
   391  				BlockTypes: map[string]*configschema.NestedBlock{
   392  					"foo": {
   393  						Nesting: configschema.NestingList,
   394  						Block: configschema.Block{
   395  							Attributes: map[string]*configschema.Attribute{
   396  								"bar": {
   397  									Type:     cty.String,
   398  									Optional: true,
   399  									Computed: true,
   400  								},
   401  								"baz": {
   402  									Type:     cty.DynamicPseudoType,
   403  									Optional: true,
   404  									Computed: true,
   405  								},
   406  							},
   407  						},
   408  					},
   409  				},
   410  			},
   411  			cty.ObjectVal(map[string]cty.Value{
   412  				"foo": cty.TupleVal([]cty.Value{
   413  					cty.ObjectVal(map[string]cty.Value{
   414  						"bar": cty.NullVal(cty.String),
   415  						"baz": cty.NumberIntVal(8),
   416  					}),
   417  				}),
   418  			}),
   419  			cty.ObjectVal(map[string]cty.Value{
   420  				"foo": cty.TupleVal([]cty.Value{
   421  					cty.ObjectVal(map[string]cty.Value{
   422  						"bar": cty.UnknownVal(cty.String),
   423  						"baz": cty.NumberIntVal(8),
   424  					}),
   425  				}),
   426  			}),
   427  		},
   428  		"prior nested map with dynamic": {
   429  			&configschema.Block{
   430  				BlockTypes: map[string]*configschema.NestedBlock{
   431  					"foo": {
   432  						Nesting: configschema.NestingMap,
   433  						Block: configschema.Block{
   434  							Attributes: map[string]*configschema.Attribute{
   435  								"bar": {
   436  									Type:     cty.String,
   437  									Optional: true,
   438  									Computed: true,
   439  								},
   440  								"baz": {
   441  									Type:     cty.DynamicPseudoType,
   442  									Optional: true,
   443  									Computed: true,
   444  								},
   445  							},
   446  						},
   447  					},
   448  				},
   449  			},
   450  			cty.ObjectVal(map[string]cty.Value{
   451  				"foo": cty.ObjectVal(map[string]cty.Value{
   452  					"a": cty.ObjectVal(map[string]cty.Value{
   453  						"bar": cty.StringVal("beep"),
   454  						"baz": cty.NullVal(cty.DynamicPseudoType),
   455  					}),
   456  					"b": cty.ObjectVal(map[string]cty.Value{
   457  						"bar": cty.StringVal("boop"),
   458  						"baz": cty.NumberIntVal(8),
   459  					}),
   460  				}),
   461  			}),
   462  			cty.ObjectVal(map[string]cty.Value{
   463  				"foo": cty.ObjectVal(map[string]cty.Value{
   464  					"a": cty.ObjectVal(map[string]cty.Value{
   465  						"bar": cty.StringVal("beep"),
   466  						"baz": cty.UnknownVal(cty.DynamicPseudoType),
   467  					}),
   468  					"b": cty.ObjectVal(map[string]cty.Value{
   469  						"bar": cty.StringVal("boop"),
   470  						"baz": cty.NumberIntVal(8),
   471  					}),
   472  				}),
   473  			}),
   474  		},
   475  	} {
   476  		t.Run(n, func(t *testing.T) {
   477  			got := SetUnknowns(tc.Val, tc.Schema)
   478  			if !got.RawEquals(tc.Expected) {
   479  				t.Fatalf("\nexpected: %#v\ngot:      %#v\n", tc.Expected, got)
   480  			}
   481  		})
   482  	}
   483  }