github.com/opentofu/opentofu@v1.7.1/internal/configs/configschema/empty_value_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 configschema
     7  
     8  import (
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/apparentlymart/go-dump/dump"
    13  	"github.com/davecgh/go-spew/spew"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  func TestBlockEmptyValue(t *testing.T) {
    18  	tests := []struct {
    19  		Schema *Block
    20  		Want   cty.Value
    21  	}{
    22  		{
    23  			&Block{},
    24  			cty.EmptyObjectVal,
    25  		},
    26  		{
    27  			&Block{
    28  				Attributes: map[string]*Attribute{
    29  					"str": {Type: cty.String, Required: true},
    30  				},
    31  			},
    32  			cty.ObjectVal(map[string]cty.Value{
    33  				"str": cty.NullVal(cty.String),
    34  			}),
    35  		},
    36  		{
    37  			&Block{
    38  				BlockTypes: map[string]*NestedBlock{
    39  					"single": {
    40  						Nesting: NestingSingle,
    41  						Block: Block{
    42  							Attributes: map[string]*Attribute{
    43  								"str": {Type: cty.String, Required: true},
    44  							},
    45  						},
    46  					},
    47  				},
    48  			},
    49  			cty.ObjectVal(map[string]cty.Value{
    50  				"single": cty.NullVal(cty.Object(map[string]cty.Type{
    51  					"str": cty.String,
    52  				})),
    53  			}),
    54  		},
    55  		{
    56  			&Block{
    57  				BlockTypes: map[string]*NestedBlock{
    58  					"group": {
    59  						Nesting: NestingGroup,
    60  						Block: Block{
    61  							Attributes: map[string]*Attribute{
    62  								"str": {Type: cty.String, Required: true},
    63  							},
    64  						},
    65  					},
    66  				},
    67  			},
    68  			cty.ObjectVal(map[string]cty.Value{
    69  				"group": cty.ObjectVal(map[string]cty.Value{
    70  					"str": cty.NullVal(cty.String),
    71  				}),
    72  			}),
    73  		},
    74  		{
    75  			&Block{
    76  				BlockTypes: map[string]*NestedBlock{
    77  					"list": {
    78  						Nesting: NestingList,
    79  						Block: Block{
    80  							Attributes: map[string]*Attribute{
    81  								"str": {Type: cty.String, Required: true},
    82  							},
    83  						},
    84  					},
    85  				},
    86  			},
    87  			cty.ObjectVal(map[string]cty.Value{
    88  				"list": cty.ListValEmpty(cty.Object(map[string]cty.Type{
    89  					"str": cty.String,
    90  				})),
    91  			}),
    92  		},
    93  		{
    94  			&Block{
    95  				BlockTypes: map[string]*NestedBlock{
    96  					"list_dynamic": {
    97  						Nesting: NestingList,
    98  						Block: Block{
    99  							Attributes: map[string]*Attribute{
   100  								"str": {Type: cty.DynamicPseudoType, Required: true},
   101  							},
   102  						},
   103  					},
   104  				},
   105  			},
   106  			cty.ObjectVal(map[string]cty.Value{
   107  				"list_dynamic": cty.EmptyTupleVal,
   108  			}),
   109  		},
   110  		{
   111  			&Block{
   112  				BlockTypes: map[string]*NestedBlock{
   113  					"map": {
   114  						Nesting: NestingMap,
   115  						Block: Block{
   116  							Attributes: map[string]*Attribute{
   117  								"str": {Type: cty.String, Required: true},
   118  							},
   119  						},
   120  					},
   121  				},
   122  			},
   123  			cty.ObjectVal(map[string]cty.Value{
   124  				"map": cty.MapValEmpty(cty.Object(map[string]cty.Type{
   125  					"str": cty.String,
   126  				})),
   127  			}),
   128  		},
   129  		{
   130  			&Block{
   131  				BlockTypes: map[string]*NestedBlock{
   132  					"map_dynamic": {
   133  						Nesting: NestingMap,
   134  						Block: Block{
   135  							Attributes: map[string]*Attribute{
   136  								"str": {Type: cty.DynamicPseudoType, Required: true},
   137  							},
   138  						},
   139  					},
   140  				},
   141  			},
   142  			cty.ObjectVal(map[string]cty.Value{
   143  				"map_dynamic": cty.EmptyObjectVal,
   144  			}),
   145  		},
   146  		{
   147  			&Block{
   148  				BlockTypes: map[string]*NestedBlock{
   149  					"set": {
   150  						Nesting: NestingSet,
   151  						Block: Block{
   152  							Attributes: map[string]*Attribute{
   153  								"str": {Type: cty.String, Required: true},
   154  							},
   155  						},
   156  					},
   157  				},
   158  			},
   159  			cty.ObjectVal(map[string]cty.Value{
   160  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{
   161  					"str": cty.String,
   162  				})),
   163  			}),
   164  		},
   165  	}
   166  
   167  	for _, test := range tests {
   168  		t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) {
   169  			got := test.Schema.EmptyValue()
   170  			if !test.Want.RawEquals(got) {
   171  				t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want))
   172  			}
   173  		})
   174  	}
   175  }
   176  
   177  // Attribute EmptyValue() is well covered by the Block tests above; these tests
   178  // focus on the behavior with NestedType field inside an Attribute
   179  func TestAttributeEmptyValue(t *testing.T) {
   180  	tests := []struct {
   181  		Schema *Attribute
   182  		Want   cty.Value
   183  	}{
   184  		{
   185  			&Attribute{},
   186  			cty.NilVal,
   187  		},
   188  		{
   189  			&Attribute{
   190  				Type: cty.String,
   191  			},
   192  			cty.NullVal(cty.String),
   193  		},
   194  		{
   195  			&Attribute{
   196  				NestedType: &Object{
   197  					Nesting: NestingSingle,
   198  					Attributes: map[string]*Attribute{
   199  						"str": {Type: cty.String, Required: true},
   200  					},
   201  				},
   202  			},
   203  			cty.NullVal(cty.Object(map[string]cty.Type{
   204  				"str": cty.String,
   205  			})),
   206  		},
   207  		{
   208  			&Attribute{
   209  				NestedType: &Object{
   210  					Nesting: NestingList,
   211  					Attributes: map[string]*Attribute{
   212  						"str": {Type: cty.String, Required: true},
   213  					},
   214  				},
   215  			},
   216  			cty.NullVal(cty.List(
   217  				cty.Object(map[string]cty.Type{
   218  					"str": cty.String,
   219  				}),
   220  			)),
   221  		},
   222  		{
   223  			&Attribute{
   224  				NestedType: &Object{
   225  					Nesting: NestingMap,
   226  					Attributes: map[string]*Attribute{
   227  						"str": {Type: cty.String, Required: true},
   228  					},
   229  				},
   230  			},
   231  			cty.NullVal(cty.Map(
   232  				cty.Object(map[string]cty.Type{
   233  					"str": cty.String,
   234  				}),
   235  			)),
   236  		},
   237  		{
   238  			&Attribute{
   239  				NestedType: &Object{
   240  					Nesting: NestingSet,
   241  					Attributes: map[string]*Attribute{
   242  						"str": {Type: cty.String, Required: true},
   243  					},
   244  				},
   245  			},
   246  			cty.NullVal(cty.Set(
   247  				cty.Object(map[string]cty.Type{
   248  					"str": cty.String,
   249  				}),
   250  			)),
   251  		},
   252  	}
   253  
   254  	for _, test := range tests {
   255  		t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) {
   256  			got := test.Schema.EmptyValue()
   257  			if !test.Want.RawEquals(got) {
   258  				t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want))
   259  			}
   260  		})
   261  	}
   262  }