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

     1  package plugin
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/big"
     7  	"strconv"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/google/go-cmp/cmp/cmpopts"
    14  	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
    15  	"github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert"
    16  	proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5"
    17  	"github.com/hashicorp/terraform-plugin-sdk/terraform"
    18  	"github.com/zclconf/go-cty/cty"
    19  	"github.com/zclconf/go-cty/cty/msgpack"
    20  )
    21  
    22  // The GRPCProviderServer will directly implement the go protobuf server
    23  var _ proto.ProviderServer = (*GRPCProviderServer)(nil)
    24  
    25  var (
    26  	valueComparer = cmp.Comparer(cty.Value.RawEquals)
    27  	equateEmpty   = cmpopts.EquateEmpty()
    28  )
    29  
    30  func TestUpgradeState_jsonState(t *testing.T) {
    31  	r := &schema.Resource{
    32  		SchemaVersion: 2,
    33  		Schema: map[string]*schema.Schema{
    34  			"two": {
    35  				Type:     schema.TypeInt,
    36  				Optional: true,
    37  			},
    38  		},
    39  	}
    40  
    41  	r.StateUpgraders = []schema.StateUpgrader{
    42  		{
    43  			Version: 0,
    44  			Type: cty.Object(map[string]cty.Type{
    45  				"id":   cty.String,
    46  				"zero": cty.Number,
    47  			}),
    48  			Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
    49  				_, ok := m["zero"].(float64)
    50  				if !ok {
    51  					return nil, fmt.Errorf("zero not found in %#v", m)
    52  				}
    53  				m["one"] = float64(1)
    54  				delete(m, "zero")
    55  				return m, nil
    56  			},
    57  		},
    58  		{
    59  			Version: 1,
    60  			Type: cty.Object(map[string]cty.Type{
    61  				"id":  cty.String,
    62  				"one": cty.Number,
    63  			}),
    64  			Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
    65  				_, ok := m["one"].(float64)
    66  				if !ok {
    67  					return nil, fmt.Errorf("one not found in %#v", m)
    68  				}
    69  				m["two"] = float64(2)
    70  				delete(m, "one")
    71  				return m, nil
    72  			},
    73  		},
    74  	}
    75  
    76  	server := &GRPCProviderServer{
    77  		provider: &schema.Provider{
    78  			ResourcesMap: map[string]*schema.Resource{
    79  				"test": r,
    80  			},
    81  		},
    82  	}
    83  
    84  	req := &proto.UpgradeResourceState_Request{
    85  		TypeName: "test",
    86  		Version:  0,
    87  		RawState: &proto.RawState{
    88  			Json: []byte(`{"id":"bar","zero":0}`),
    89  		},
    90  	}
    91  
    92  	resp, err := server.UpgradeResourceState(nil, req)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  
    97  	if len(resp.Diagnostics) > 0 {
    98  		for _, d := range resp.Diagnostics {
    99  			t.Errorf("%#v", d)
   100  		}
   101  		t.Fatal("error")
   102  	}
   103  
   104  	val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  
   109  	expected := cty.ObjectVal(map[string]cty.Value{
   110  		"id":  cty.StringVal("bar"),
   111  		"two": cty.NumberIntVal(2),
   112  	})
   113  
   114  	if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
   115  		t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
   116  	}
   117  }
   118  
   119  func TestUpgradeState_jsonStateBigInt(t *testing.T) {
   120  	r := &schema.Resource{
   121  		UseJSONNumber: true,
   122  		SchemaVersion: 2,
   123  		Schema: map[string]*schema.Schema{
   124  			"int": {
   125  				Type:     schema.TypeInt,
   126  				Required: true,
   127  			},
   128  		},
   129  	}
   130  
   131  	server := NewGRPCProviderServerShim(&schema.Provider{
   132  		ResourcesMap: map[string]*schema.Resource{
   133  			"test": r,
   134  		},
   135  	})
   136  
   137  	req := &proto.UpgradeResourceState_Request{
   138  		TypeName: "test",
   139  		Version:  0,
   140  		RawState: &proto.RawState{
   141  			Json: []byte(`{"id":"bar","int":7227701560655103598}`),
   142  		},
   143  	}
   144  
   145  	resp, err := server.UpgradeResourceState(nil, req)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  
   150  	if len(resp.Diagnostics) > 0 {
   151  		for _, d := range resp.Diagnostics {
   152  			t.Errorf("%#v", d)
   153  		}
   154  		t.Fatal("error")
   155  	}
   156  
   157  	val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
   158  	if err != nil {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	expected := cty.ObjectVal(map[string]cty.Value{
   163  		"id":  cty.StringVal("bar"),
   164  		"int": cty.NumberIntVal(7227701560655103598),
   165  	})
   166  
   167  	if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
   168  		t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
   169  	}
   170  }
   171  
   172  func TestUpgradeState_removedAttr(t *testing.T) {
   173  	r1 := &schema.Resource{
   174  		Schema: map[string]*schema.Schema{
   175  			"two": {
   176  				Type:     schema.TypeString,
   177  				Optional: true,
   178  			},
   179  		},
   180  	}
   181  
   182  	r2 := &schema.Resource{
   183  		Schema: map[string]*schema.Schema{
   184  			"multi": {
   185  				Type:     schema.TypeSet,
   186  				Optional: true,
   187  				Elem: &schema.Resource{
   188  					Schema: map[string]*schema.Schema{
   189  						"set": {
   190  							Type:     schema.TypeSet,
   191  							Optional: true,
   192  							Elem: &schema.Resource{
   193  								Schema: map[string]*schema.Schema{
   194  									"required": {
   195  										Type:     schema.TypeString,
   196  										Required: true,
   197  									},
   198  								},
   199  							},
   200  						},
   201  					},
   202  				},
   203  			},
   204  		},
   205  	}
   206  
   207  	r3 := &schema.Resource{
   208  		Schema: map[string]*schema.Schema{
   209  			"config_mode_attr": {
   210  				Type:       schema.TypeList,
   211  				ConfigMode: schema.SchemaConfigModeAttr,
   212  				Optional:   true,
   213  				Elem: &schema.Resource{
   214  					Schema: map[string]*schema.Schema{
   215  						"foo": {
   216  							Type:     schema.TypeString,
   217  							Optional: true,
   218  						},
   219  					},
   220  				},
   221  			},
   222  		},
   223  	}
   224  
   225  	p := &schema.Provider{
   226  		ResourcesMap: map[string]*schema.Resource{
   227  			"r1": r1,
   228  			"r2": r2,
   229  			"r3": r3,
   230  		},
   231  	}
   232  
   233  	server := &GRPCProviderServer{
   234  		provider: p,
   235  	}
   236  
   237  	for _, tc := range []struct {
   238  		name     string
   239  		raw      string
   240  		expected cty.Value
   241  	}{
   242  		{
   243  			name: "r1",
   244  			raw:  `{"id":"bar","removed":"removed","two":"2"}`,
   245  			expected: cty.ObjectVal(map[string]cty.Value{
   246  				"id":  cty.StringVal("bar"),
   247  				"two": cty.StringVal("2"),
   248  			}),
   249  		},
   250  		{
   251  			name: "r2",
   252  			raw:  `{"id":"bar","multi":[{"set":[{"required":"ok","removed":"removed"}]}]}`,
   253  			expected: cty.ObjectVal(map[string]cty.Value{
   254  				"id": cty.StringVal("bar"),
   255  				"multi": cty.SetVal([]cty.Value{
   256  					cty.ObjectVal(map[string]cty.Value{
   257  						"set": cty.SetVal([]cty.Value{
   258  							cty.ObjectVal(map[string]cty.Value{
   259  								"required": cty.StringVal("ok"),
   260  							}),
   261  						}),
   262  					}),
   263  				}),
   264  			}),
   265  		},
   266  		{
   267  			name: "r3",
   268  			raw:  `{"id":"bar","config_mode_attr":[{"foo":"ok","removed":"removed"}]}`,
   269  			expected: cty.ObjectVal(map[string]cty.Value{
   270  				"id": cty.StringVal("bar"),
   271  				"config_mode_attr": cty.ListVal([]cty.Value{
   272  					cty.ObjectVal(map[string]cty.Value{
   273  						"foo": cty.StringVal("ok"),
   274  					}),
   275  				}),
   276  			}),
   277  		},
   278  	} {
   279  		t.Run(tc.name, func(t *testing.T) {
   280  			req := &proto.UpgradeResourceState_Request{
   281  				TypeName: tc.name,
   282  				Version:  0,
   283  				RawState: &proto.RawState{
   284  					Json: []byte(tc.raw),
   285  				},
   286  			}
   287  			resp, err := server.UpgradeResourceState(nil, req)
   288  			if err != nil {
   289  				t.Fatal(err)
   290  			}
   291  
   292  			if len(resp.Diagnostics) > 0 {
   293  				for _, d := range resp.Diagnostics {
   294  					t.Errorf("%#v", d)
   295  				}
   296  				t.Fatal("error")
   297  			}
   298  			val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, p.ResourcesMap[tc.name].CoreConfigSchema().ImpliedType())
   299  			if err != nil {
   300  				t.Fatal(err)
   301  			}
   302  			if !tc.expected.RawEquals(val) {
   303  				t.Fatalf("\nexpected: %#v\ngot:      %#v\n", tc.expected, val)
   304  			}
   305  		})
   306  	}
   307  
   308  }
   309  
   310  func TestUpgradeState_flatmapState(t *testing.T) {
   311  	r := &schema.Resource{
   312  		SchemaVersion: 4,
   313  		Schema: map[string]*schema.Schema{
   314  			"four": {
   315  				Type:     schema.TypeInt,
   316  				Required: true,
   317  			},
   318  			"block": {
   319  				Type:     schema.TypeList,
   320  				Optional: true,
   321  				Elem: &schema.Resource{
   322  					Schema: map[string]*schema.Schema{
   323  						"attr": {
   324  							Type:     schema.TypeString,
   325  							Optional: true,
   326  						},
   327  					},
   328  				},
   329  			},
   330  		},
   331  		// this MigrateState will take the state to version 2
   332  		MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) {
   333  			switch v {
   334  			case 0:
   335  				_, ok := is.Attributes["zero"]
   336  				if !ok {
   337  					return nil, fmt.Errorf("zero not found in %#v", is.Attributes)
   338  				}
   339  				is.Attributes["one"] = "1"
   340  				delete(is.Attributes, "zero")
   341  				fallthrough
   342  			case 1:
   343  				_, ok := is.Attributes["one"]
   344  				if !ok {
   345  					return nil, fmt.Errorf("one not found in %#v", is.Attributes)
   346  				}
   347  				is.Attributes["two"] = "2"
   348  				delete(is.Attributes, "one")
   349  			default:
   350  				return nil, fmt.Errorf("invalid schema version %d", v)
   351  			}
   352  			return is, nil
   353  		},
   354  	}
   355  
   356  	r.StateUpgraders = []schema.StateUpgrader{
   357  		{
   358  			Version: 2,
   359  			Type: cty.Object(map[string]cty.Type{
   360  				"id":  cty.String,
   361  				"two": cty.Number,
   362  			}),
   363  			Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
   364  				_, ok := m["two"].(float64)
   365  				if !ok {
   366  					return nil, fmt.Errorf("two not found in %#v", m)
   367  				}
   368  				m["three"] = float64(3)
   369  				delete(m, "two")
   370  				return m, nil
   371  			},
   372  		},
   373  		{
   374  			Version: 3,
   375  			Type: cty.Object(map[string]cty.Type{
   376  				"id":    cty.String,
   377  				"three": cty.Number,
   378  			}),
   379  			Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
   380  				_, ok := m["three"].(float64)
   381  				if !ok {
   382  					return nil, fmt.Errorf("three not found in %#v", m)
   383  				}
   384  				m["four"] = float64(4)
   385  				delete(m, "three")
   386  				return m, nil
   387  			},
   388  		},
   389  	}
   390  
   391  	server := &GRPCProviderServer{
   392  		provider: &schema.Provider{
   393  			ResourcesMap: map[string]*schema.Resource{
   394  				"test": r,
   395  			},
   396  		},
   397  	}
   398  
   399  	testReqs := []*proto.UpgradeResourceState_Request{
   400  		{
   401  			TypeName: "test",
   402  			Version:  0,
   403  			RawState: &proto.RawState{
   404  				Flatmap: map[string]string{
   405  					"id":   "bar",
   406  					"zero": "0",
   407  				},
   408  			},
   409  		},
   410  		{
   411  			TypeName: "test",
   412  			Version:  1,
   413  			RawState: &proto.RawState{
   414  				Flatmap: map[string]string{
   415  					"id":  "bar",
   416  					"one": "1",
   417  				},
   418  			},
   419  		},
   420  		// two and  up could be stored in flatmap or json states
   421  		{
   422  			TypeName: "test",
   423  			Version:  2,
   424  			RawState: &proto.RawState{
   425  				Flatmap: map[string]string{
   426  					"id":  "bar",
   427  					"two": "2",
   428  				},
   429  			},
   430  		},
   431  		{
   432  			TypeName: "test",
   433  			Version:  2,
   434  			RawState: &proto.RawState{
   435  				Json: []byte(`{"id":"bar","two":2}`),
   436  			},
   437  		},
   438  		{
   439  			TypeName: "test",
   440  			Version:  3,
   441  			RawState: &proto.RawState{
   442  				Flatmap: map[string]string{
   443  					"id":    "bar",
   444  					"three": "3",
   445  				},
   446  			},
   447  		},
   448  		{
   449  			TypeName: "test",
   450  			Version:  3,
   451  			RawState: &proto.RawState{
   452  				Json: []byte(`{"id":"bar","three":3}`),
   453  			},
   454  		},
   455  		{
   456  			TypeName: "test",
   457  			Version:  4,
   458  			RawState: &proto.RawState{
   459  				Flatmap: map[string]string{
   460  					"id":   "bar",
   461  					"four": "4",
   462  				},
   463  			},
   464  		},
   465  		{
   466  			TypeName: "test",
   467  			Version:  4,
   468  			RawState: &proto.RawState{
   469  				Json: []byte(`{"id":"bar","four":4}`),
   470  			},
   471  		},
   472  	}
   473  
   474  	for i, req := range testReqs {
   475  		t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) {
   476  			resp, err := server.UpgradeResourceState(nil, req)
   477  			if err != nil {
   478  				t.Fatal(err)
   479  			}
   480  
   481  			if len(resp.Diagnostics) > 0 {
   482  				for _, d := range resp.Diagnostics {
   483  					t.Errorf("%#v", d)
   484  				}
   485  				t.Fatal("error")
   486  			}
   487  
   488  			val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
   489  			if err != nil {
   490  				t.Fatal(err)
   491  			}
   492  
   493  			expected := cty.ObjectVal(map[string]cty.Value{
   494  				"block": cty.ListValEmpty(cty.Object(map[string]cty.Type{"attr": cty.String})),
   495  				"id":    cty.StringVal("bar"),
   496  				"four":  cty.NumberIntVal(4),
   497  			})
   498  
   499  			if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
   500  				t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
   501  			}
   502  		})
   503  	}
   504  }
   505  
   506  func TestUpgradeState_flatmapStateMissingMigrateState(t *testing.T) {
   507  	r := &schema.Resource{
   508  		SchemaVersion: 1,
   509  		Schema: map[string]*schema.Schema{
   510  			"one": {
   511  				Type:     schema.TypeInt,
   512  				Required: true,
   513  			},
   514  		},
   515  	}
   516  
   517  	server := &GRPCProviderServer{
   518  		provider: &schema.Provider{
   519  			ResourcesMap: map[string]*schema.Resource{
   520  				"test": r,
   521  			},
   522  		},
   523  	}
   524  
   525  	testReqs := []*proto.UpgradeResourceState_Request{
   526  		{
   527  			TypeName: "test",
   528  			Version:  0,
   529  			RawState: &proto.RawState{
   530  				Flatmap: map[string]string{
   531  					"id":  "bar",
   532  					"one": "1",
   533  				},
   534  			},
   535  		},
   536  		{
   537  			TypeName: "test",
   538  			Version:  1,
   539  			RawState: &proto.RawState{
   540  				Flatmap: map[string]string{
   541  					"id":  "bar",
   542  					"one": "1",
   543  				},
   544  			},
   545  		},
   546  		{
   547  			TypeName: "test",
   548  			Version:  1,
   549  			RawState: &proto.RawState{
   550  				Json: []byte(`{"id":"bar","one":1}`),
   551  			},
   552  		},
   553  	}
   554  
   555  	for i, req := range testReqs {
   556  		t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) {
   557  			resp, err := server.UpgradeResourceState(nil, req)
   558  			if err != nil {
   559  				t.Fatal(err)
   560  			}
   561  
   562  			if len(resp.Diagnostics) > 0 {
   563  				for _, d := range resp.Diagnostics {
   564  					t.Errorf("%#v", d)
   565  				}
   566  				t.Fatal("error")
   567  			}
   568  
   569  			val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
   570  			if err != nil {
   571  				t.Fatal(err)
   572  			}
   573  
   574  			expected := cty.ObjectVal(map[string]cty.Value{
   575  				"id":  cty.StringVal("bar"),
   576  				"one": cty.NumberIntVal(1),
   577  			})
   578  
   579  			if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
   580  				t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
   581  			}
   582  		})
   583  	}
   584  }
   585  
   586  func TestPlanResourceChange(t *testing.T) {
   587  	r := &schema.Resource{
   588  		SchemaVersion: 4,
   589  		Schema: map[string]*schema.Schema{
   590  			"foo": {
   591  				Type:     schema.TypeInt,
   592  				Optional: true,
   593  			},
   594  		},
   595  	}
   596  
   597  	server := &GRPCProviderServer{
   598  		provider: &schema.Provider{
   599  			ResourcesMap: map[string]*schema.Resource{
   600  				"test": r,
   601  			},
   602  		},
   603  	}
   604  
   605  	schema := r.CoreConfigSchema()
   606  	priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  
   611  	// A propsed state with only the ID unknown will produce a nil diff, and
   612  	// should return the propsed state value.
   613  	proposedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   614  		"id": cty.UnknownVal(cty.String),
   615  	}))
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  	proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType())
   620  	if err != nil {
   621  		t.Fatal(err)
   622  	}
   623  
   624  	testReq := &proto.PlanResourceChange_Request{
   625  		TypeName: "test",
   626  		PriorState: &proto.DynamicValue{
   627  			Msgpack: priorState,
   628  		},
   629  		ProposedNewState: &proto.DynamicValue{
   630  			Msgpack: proposedState,
   631  		},
   632  	}
   633  
   634  	resp, err := server.PlanResourceChange(context.Background(), testReq)
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  
   639  	plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.Msgpack, schema.ImpliedType())
   640  	if err != nil {
   641  		t.Fatal(err)
   642  	}
   643  
   644  	if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) {
   645  		t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer))
   646  	}
   647  }
   648  
   649  func TestPlanResourceChange_bigint(t *testing.T) {
   650  	r := &schema.Resource{
   651  		UseJSONNumber: true,
   652  		Schema: map[string]*schema.Schema{
   653  			"foo": {
   654  				Type:     schema.TypeInt,
   655  				Required: true,
   656  			},
   657  		},
   658  	}
   659  
   660  	server := NewGRPCProviderServerShim(&schema.Provider{
   661  		ResourcesMap: map[string]*schema.Resource{
   662  			"test": r,
   663  		},
   664  	})
   665  
   666  	schema := r.CoreConfigSchema()
   667  	priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
   668  	if err != nil {
   669  		t.Fatal(err)
   670  	}
   671  
   672  	proposedVal := cty.ObjectVal(map[string]cty.Value{
   673  		"id":  cty.UnknownVal(cty.String),
   674  		"foo": cty.MustParseNumberVal("7227701560655103598"),
   675  	})
   676  	proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType())
   677  	if err != nil {
   678  		t.Fatal(err)
   679  	}
   680  
   681  	testReq := &proto.PlanResourceChange_Request{
   682  		TypeName: "test",
   683  		PriorState: &proto.DynamicValue{
   684  			Msgpack: priorState,
   685  		},
   686  		ProposedNewState: &proto.DynamicValue{
   687  			Msgpack: proposedState,
   688  		},
   689  	}
   690  
   691  	resp, err := server.PlanResourceChange(context.Background(), testReq)
   692  	if err != nil {
   693  		t.Fatal(err)
   694  	}
   695  
   696  	plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.Msgpack, schema.ImpliedType())
   697  	if err != nil {
   698  		t.Fatal(err)
   699  	}
   700  
   701  	if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) {
   702  		t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer))
   703  	}
   704  
   705  	plannedStateFoo, acc := plannedStateVal.GetAttr("foo").AsBigFloat().Int64()
   706  	if acc != big.Exact {
   707  		t.Fatalf("Expected exact accuracy, got %s", acc)
   708  	}
   709  	if plannedStateFoo != 7227701560655103598 {
   710  		t.Fatalf("Expected %d, got %d, this represents a loss of precision in planning large numbers", 7227701560655103598, plannedStateFoo)
   711  	}
   712  }
   713  
   714  func TestApplyResourceChange(t *testing.T) {
   715  	r := &schema.Resource{
   716  		SchemaVersion: 4,
   717  		Schema: map[string]*schema.Schema{
   718  			"foo": {
   719  				Type:     schema.TypeInt,
   720  				Optional: true,
   721  			},
   722  		},
   723  		Create: func(rd *schema.ResourceData, _ interface{}) error {
   724  			rd.SetId("bar")
   725  			return nil
   726  		},
   727  	}
   728  
   729  	server := &GRPCProviderServer{
   730  		provider: &schema.Provider{
   731  			ResourcesMap: map[string]*schema.Resource{
   732  				"test": r,
   733  			},
   734  		},
   735  	}
   736  
   737  	schema := r.CoreConfigSchema()
   738  	priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
   739  	if err != nil {
   740  		t.Fatal(err)
   741  	}
   742  
   743  	// A proposed state with only the ID unknown will produce a nil diff, and
   744  	// should return the proposed state value.
   745  	plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   746  		"id": cty.UnknownVal(cty.String),
   747  	}))
   748  	if err != nil {
   749  		t.Fatal(err)
   750  	}
   751  	plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType())
   752  	if err != nil {
   753  		t.Fatal(err)
   754  	}
   755  
   756  	testReq := &proto.ApplyResourceChange_Request{
   757  		TypeName: "test",
   758  		PriorState: &proto.DynamicValue{
   759  			Msgpack: priorState,
   760  		},
   761  		PlannedState: &proto.DynamicValue{
   762  			Msgpack: plannedState,
   763  		},
   764  	}
   765  
   766  	resp, err := server.ApplyResourceChange(context.Background(), testReq)
   767  	if err != nil {
   768  		t.Fatal(err)
   769  	}
   770  
   771  	newStateVal, err := msgpack.Unmarshal(resp.NewState.Msgpack, schema.ImpliedType())
   772  	if err != nil {
   773  		t.Fatal(err)
   774  	}
   775  
   776  	id := newStateVal.GetAttr("id").AsString()
   777  	if id != "bar" {
   778  		t.Fatalf("incorrect final state: %#v\n", newStateVal)
   779  	}
   780  }
   781  
   782  func TestApplyResourceChange_bigint(t *testing.T) {
   783  	r := &schema.Resource{
   784  		UseJSONNumber: true,
   785  		Schema: map[string]*schema.Schema{
   786  			"foo": {
   787  				Type:     schema.TypeInt,
   788  				Required: true,
   789  			},
   790  		},
   791  		Create: func(rd *schema.ResourceData, _ interface{}) error {
   792  			rd.SetId("bar")
   793  			return nil
   794  		},
   795  	}
   796  
   797  	server := NewGRPCProviderServerShim(&schema.Provider{
   798  		ResourcesMap: map[string]*schema.Resource{
   799  			"test": r,
   800  		},
   801  	})
   802  
   803  	schema := r.CoreConfigSchema()
   804  	priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
   805  	if err != nil {
   806  		t.Fatal(err)
   807  	}
   808  
   809  	plannedVal := cty.ObjectVal(map[string]cty.Value{
   810  		"id":  cty.UnknownVal(cty.String),
   811  		"foo": cty.MustParseNumberVal("7227701560655103598"),
   812  	})
   813  	plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType())
   814  	if err != nil {
   815  		t.Fatal(err)
   816  	}
   817  
   818  	testReq := &proto.ApplyResourceChange_Request{
   819  		TypeName: "test",
   820  		PriorState: &proto.DynamicValue{
   821  			Msgpack: priorState,
   822  		},
   823  		PlannedState: &proto.DynamicValue{
   824  			Msgpack: plannedState,
   825  		},
   826  	}
   827  
   828  	resp, err := server.ApplyResourceChange(context.Background(), testReq)
   829  	if err != nil {
   830  		t.Fatal(err)
   831  	}
   832  
   833  	newStateVal, err := msgpack.Unmarshal(resp.NewState.Msgpack, schema.ImpliedType())
   834  	if err != nil {
   835  		t.Fatal(err)
   836  	}
   837  
   838  	id := newStateVal.GetAttr("id").AsString()
   839  	if id != "bar" {
   840  		t.Fatalf("incorrect final state: %#v\n", newStateVal)
   841  	}
   842  
   843  	foo, acc := newStateVal.GetAttr("foo").AsBigFloat().Int64()
   844  	if acc != big.Exact {
   845  		t.Fatalf("Expected exact accuracy, got %s", acc)
   846  	}
   847  	if foo != 7227701560655103598 {
   848  		t.Fatalf("Expected %d, got %d, this represents a loss of precision in applying large numbers", 7227701560655103598, foo)
   849  	}
   850  }
   851  
   852  func TestPrepareProviderConfig(t *testing.T) {
   853  	for _, tc := range []struct {
   854  		Name         string
   855  		Schema       map[string]*schema.Schema
   856  		ConfigVal    cty.Value
   857  		ExpectError  string
   858  		ExpectConfig cty.Value
   859  	}{
   860  		{
   861  			Name: "test prepare",
   862  			Schema: map[string]*schema.Schema{
   863  				"foo": {
   864  					Type:     schema.TypeString,
   865  					Optional: true,
   866  				},
   867  			},
   868  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   869  				"foo": cty.StringVal("bar"),
   870  			}),
   871  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   872  				"foo": cty.StringVal("bar"),
   873  			}),
   874  		},
   875  		{
   876  			Name: "test default",
   877  			Schema: map[string]*schema.Schema{
   878  				"foo": {
   879  					Type:     schema.TypeString,
   880  					Optional: true,
   881  					Default:  "default",
   882  				},
   883  			},
   884  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   885  				"foo": cty.NullVal(cty.String),
   886  			}),
   887  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   888  				"foo": cty.StringVal("default"),
   889  			}),
   890  		},
   891  		{
   892  			Name: "test defaultfunc",
   893  			Schema: map[string]*schema.Schema{
   894  				"foo": {
   895  					Type:     schema.TypeString,
   896  					Optional: true,
   897  					DefaultFunc: func() (interface{}, error) {
   898  						return "defaultfunc", nil
   899  					},
   900  				},
   901  			},
   902  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   903  				"foo": cty.NullVal(cty.String),
   904  			}),
   905  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   906  				"foo": cty.StringVal("defaultfunc"),
   907  			}),
   908  		},
   909  		{
   910  			Name: "test default required",
   911  			Schema: map[string]*schema.Schema{
   912  				"foo": {
   913  					Type:     schema.TypeString,
   914  					Required: true,
   915  					DefaultFunc: func() (interface{}, error) {
   916  						return "defaultfunc", nil
   917  					},
   918  				},
   919  			},
   920  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   921  				"foo": cty.NullVal(cty.String),
   922  			}),
   923  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   924  				"foo": cty.StringVal("defaultfunc"),
   925  			}),
   926  		},
   927  		{
   928  			Name: "test incorrect type",
   929  			Schema: map[string]*schema.Schema{
   930  				"foo": {
   931  					Type:     schema.TypeString,
   932  					Required: true,
   933  				},
   934  			},
   935  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   936  				"foo": cty.NumberIntVal(3),
   937  			}),
   938  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   939  				"foo": cty.StringVal("3"),
   940  			}),
   941  		},
   942  		{
   943  			Name: "test incorrect default type",
   944  			Schema: map[string]*schema.Schema{
   945  				"foo": {
   946  					Type:     schema.TypeString,
   947  					Optional: true,
   948  					Default:  true,
   949  				},
   950  			},
   951  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   952  				"foo": cty.NullVal(cty.String),
   953  			}),
   954  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   955  				"foo": cty.StringVal("true"),
   956  			}),
   957  		},
   958  		{
   959  			Name: "test incorrect default bool type",
   960  			Schema: map[string]*schema.Schema{
   961  				"foo": {
   962  					Type:     schema.TypeBool,
   963  					Optional: true,
   964  					Default:  "",
   965  				},
   966  			},
   967  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   968  				"foo": cty.NullVal(cty.Bool),
   969  			}),
   970  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   971  				"foo": cty.False,
   972  			}),
   973  		},
   974  		{
   975  			Name: "test deprecated default",
   976  			Schema: map[string]*schema.Schema{
   977  				"foo": {
   978  					Type:     schema.TypeString,
   979  					Optional: true,
   980  					Default:  "do not use",
   981  					Removed:  "don't use this",
   982  				},
   983  			},
   984  			ConfigVal: cty.ObjectVal(map[string]cty.Value{
   985  				"foo": cty.NullVal(cty.String),
   986  			}),
   987  			ExpectConfig: cty.ObjectVal(map[string]cty.Value{
   988  				"foo": cty.NullVal(cty.String),
   989  			}),
   990  		},
   991  	} {
   992  		t.Run(tc.Name, func(t *testing.T) {
   993  			server := &GRPCProviderServer{
   994  				provider: &schema.Provider{
   995  					Schema: tc.Schema,
   996  				},
   997  			}
   998  
   999  			block := schema.InternalMap(tc.Schema).CoreConfigSchema()
  1000  
  1001  			rawConfig, err := msgpack.Marshal(tc.ConfigVal, block.ImpliedType())
  1002  			if err != nil {
  1003  				t.Fatal(err)
  1004  			}
  1005  
  1006  			testReq := &proto.PrepareProviderConfig_Request{
  1007  				Config: &proto.DynamicValue{
  1008  					Msgpack: rawConfig,
  1009  				},
  1010  			}
  1011  
  1012  			resp, err := server.PrepareProviderConfig(nil, testReq)
  1013  			if err != nil {
  1014  				t.Fatal(err)
  1015  			}
  1016  
  1017  			if tc.ExpectError != "" && len(resp.Diagnostics) > 0 {
  1018  				for _, d := range resp.Diagnostics {
  1019  					if !strings.Contains(d.Summary, tc.ExpectError) {
  1020  						t.Fatalf("Unexpected error: %s/%s", d.Summary, d.Detail)
  1021  					}
  1022  				}
  1023  				return
  1024  			}
  1025  
  1026  			// we should have no errors past this point
  1027  			for _, d := range resp.Diagnostics {
  1028  				if d.Severity == proto.Diagnostic_ERROR {
  1029  					t.Fatal(resp.Diagnostics)
  1030  				}
  1031  			}
  1032  
  1033  			val, err := msgpack.Unmarshal(resp.PreparedConfig.Msgpack, block.ImpliedType())
  1034  			if err != nil {
  1035  				t.Fatal(err)
  1036  			}
  1037  
  1038  			if tc.ExpectConfig.GoString() != val.GoString() {
  1039  				t.Fatalf("\nexpected: %#v\ngot: %#v", tc.ExpectConfig, val)
  1040  			}
  1041  		})
  1042  	}
  1043  }
  1044  
  1045  func TestGetSchemaTimeouts(t *testing.T) {
  1046  	r := &schema.Resource{
  1047  		SchemaVersion: 4,
  1048  		Timeouts: &schema.ResourceTimeout{
  1049  			Create:  schema.DefaultTimeout(time.Second),
  1050  			Read:    schema.DefaultTimeout(2 * time.Second),
  1051  			Update:  schema.DefaultTimeout(3 * time.Second),
  1052  			Default: schema.DefaultTimeout(10 * time.Second),
  1053  		},
  1054  		Schema: map[string]*schema.Schema{
  1055  			"foo": {
  1056  				Type:     schema.TypeInt,
  1057  				Optional: true,
  1058  			},
  1059  		},
  1060  	}
  1061  
  1062  	// verify that the timeouts appear in the schema as defined
  1063  	block := r.CoreConfigSchema()
  1064  	timeoutsBlock := block.BlockTypes["timeouts"]
  1065  	if timeoutsBlock == nil {
  1066  		t.Fatal("missing timeouts in schema")
  1067  	}
  1068  
  1069  	if timeoutsBlock.Attributes["create"] == nil {
  1070  		t.Fatal("missing create timeout in schema")
  1071  	}
  1072  	if timeoutsBlock.Attributes["read"] == nil {
  1073  		t.Fatal("missing read timeout in schema")
  1074  	}
  1075  	if timeoutsBlock.Attributes["update"] == nil {
  1076  		t.Fatal("missing update timeout in schema")
  1077  	}
  1078  	if d := timeoutsBlock.Attributes["delete"]; d != nil {
  1079  		t.Fatalf("unexpected delete timeout in schema: %#v", d)
  1080  	}
  1081  	if timeoutsBlock.Attributes["default"] == nil {
  1082  		t.Fatal("missing default timeout in schema")
  1083  	}
  1084  }
  1085  
  1086  func TestNormalizeNullValues(t *testing.T) {
  1087  	for i, tc := range []struct {
  1088  		Src, Dst, Expect cty.Value
  1089  		Apply            bool
  1090  	}{
  1091  		{
  1092  			// The known set value is copied over the null set value
  1093  			Src: cty.ObjectVal(map[string]cty.Value{
  1094  				"set": cty.SetVal([]cty.Value{
  1095  					cty.ObjectVal(map[string]cty.Value{
  1096  						"foo": cty.NullVal(cty.String),
  1097  					}),
  1098  				}),
  1099  			}),
  1100  			Dst: cty.ObjectVal(map[string]cty.Value{
  1101  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1102  					"foo": cty.String,
  1103  				}))),
  1104  			}),
  1105  			Expect: cty.ObjectVal(map[string]cty.Value{
  1106  				"set": cty.SetVal([]cty.Value{
  1107  					cty.ObjectVal(map[string]cty.Value{
  1108  						"foo": cty.NullVal(cty.String),
  1109  					}),
  1110  				}),
  1111  			}),
  1112  			Apply: true,
  1113  		},
  1114  		{
  1115  			// A zero set value is kept
  1116  			Src: cty.ObjectVal(map[string]cty.Value{
  1117  				"set": cty.SetValEmpty(cty.String),
  1118  			}),
  1119  			Dst: cty.ObjectVal(map[string]cty.Value{
  1120  				"set": cty.SetValEmpty(cty.String),
  1121  			}),
  1122  			Expect: cty.ObjectVal(map[string]cty.Value{
  1123  				"set": cty.SetValEmpty(cty.String),
  1124  			}),
  1125  		},
  1126  		{
  1127  			// The known set value is copied over the null set value
  1128  			Src: cty.ObjectVal(map[string]cty.Value{
  1129  				"set": cty.SetVal([]cty.Value{
  1130  					cty.ObjectVal(map[string]cty.Value{
  1131  						"foo": cty.NullVal(cty.String),
  1132  					}),
  1133  				}),
  1134  			}),
  1135  			Dst: cty.ObjectVal(map[string]cty.Value{
  1136  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1137  					"foo": cty.String,
  1138  				}))),
  1139  			}),
  1140  			// If we're only in a plan, we can't compare sets at all
  1141  			Expect: cty.ObjectVal(map[string]cty.Value{
  1142  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1143  					"foo": cty.String,
  1144  				}))),
  1145  			}),
  1146  		},
  1147  		{
  1148  			// The empty map is copied over the null map
  1149  			Src: cty.ObjectVal(map[string]cty.Value{
  1150  				"map": cty.MapValEmpty(cty.String),
  1151  			}),
  1152  			Dst: cty.ObjectVal(map[string]cty.Value{
  1153  				"map": cty.NullVal(cty.Map(cty.String)),
  1154  			}),
  1155  			Expect: cty.ObjectVal(map[string]cty.Value{
  1156  				"map": cty.MapValEmpty(cty.String),
  1157  			}),
  1158  			Apply: true,
  1159  		},
  1160  		{
  1161  			// A zero value primitive is copied over a null primitive
  1162  			Src: cty.ObjectVal(map[string]cty.Value{
  1163  				"string": cty.StringVal(""),
  1164  			}),
  1165  			Dst: cty.ObjectVal(map[string]cty.Value{
  1166  				"string": cty.NullVal(cty.String),
  1167  			}),
  1168  			Expect: cty.ObjectVal(map[string]cty.Value{
  1169  				"string": cty.StringVal(""),
  1170  			}),
  1171  			Apply: true,
  1172  		},
  1173  		{
  1174  			// Plan primitives are kept
  1175  			Src: cty.ObjectVal(map[string]cty.Value{
  1176  				"string": cty.NumberIntVal(0),
  1177  			}),
  1178  			Dst: cty.ObjectVal(map[string]cty.Value{
  1179  				"string": cty.NullVal(cty.Number),
  1180  			}),
  1181  			Expect: cty.ObjectVal(map[string]cty.Value{
  1182  				"string": cty.NullVal(cty.Number),
  1183  			}),
  1184  		},
  1185  		{
  1186  			// Neither plan nor apply should remove empty strings
  1187  			Src: cty.ObjectVal(map[string]cty.Value{
  1188  				"string": cty.StringVal(""),
  1189  			}),
  1190  			Dst: cty.ObjectVal(map[string]cty.Value{
  1191  				"string": cty.NullVal(cty.String),
  1192  			}),
  1193  			Expect: cty.ObjectVal(map[string]cty.Value{
  1194  				"string": cty.StringVal(""),
  1195  			}),
  1196  		},
  1197  		{
  1198  			// Neither plan nor apply should remove empty strings
  1199  			Src: cty.ObjectVal(map[string]cty.Value{
  1200  				"string": cty.StringVal(""),
  1201  			}),
  1202  			Dst: cty.ObjectVal(map[string]cty.Value{
  1203  				"string": cty.NullVal(cty.String),
  1204  			}),
  1205  			Expect: cty.ObjectVal(map[string]cty.Value{
  1206  				"string": cty.StringVal(""),
  1207  			}),
  1208  			Apply: true,
  1209  		},
  1210  		{
  1211  			// The null map is retained, because the src was unknown
  1212  			Src: cty.ObjectVal(map[string]cty.Value{
  1213  				"map": cty.UnknownVal(cty.Map(cty.String)),
  1214  			}),
  1215  			Dst: cty.ObjectVal(map[string]cty.Value{
  1216  				"map": cty.NullVal(cty.Map(cty.String)),
  1217  			}),
  1218  			Expect: cty.ObjectVal(map[string]cty.Value{
  1219  				"map": cty.NullVal(cty.Map(cty.String)),
  1220  			}),
  1221  			Apply: true,
  1222  		},
  1223  		{
  1224  			// the nul set is retained, because the src set contains an unknown value
  1225  			Src: cty.ObjectVal(map[string]cty.Value{
  1226  				"set": cty.SetVal([]cty.Value{
  1227  					cty.ObjectVal(map[string]cty.Value{
  1228  						"foo": cty.UnknownVal(cty.String),
  1229  					}),
  1230  				}),
  1231  			}),
  1232  			Dst: cty.ObjectVal(map[string]cty.Value{
  1233  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1234  					"foo": cty.String,
  1235  				}))),
  1236  			}),
  1237  			Expect: cty.ObjectVal(map[string]cty.Value{
  1238  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1239  					"foo": cty.String,
  1240  				}))),
  1241  			}),
  1242  			Apply: true,
  1243  		},
  1244  		{
  1245  			// Retain don't re-add unexpected planned values in a map
  1246  			Src: cty.ObjectVal(map[string]cty.Value{
  1247  				"map": cty.MapVal(map[string]cty.Value{
  1248  					"a": cty.StringVal("a"),
  1249  					"b": cty.StringVal(""),
  1250  				}),
  1251  			}),
  1252  			Dst: cty.ObjectVal(map[string]cty.Value{
  1253  				"map": cty.MapVal(map[string]cty.Value{
  1254  					"a": cty.StringVal("a"),
  1255  				}),
  1256  			}),
  1257  			Expect: cty.ObjectVal(map[string]cty.Value{
  1258  				"map": cty.MapVal(map[string]cty.Value{
  1259  					"a": cty.StringVal("a"),
  1260  				}),
  1261  			}),
  1262  		},
  1263  		{
  1264  			// Remove extra values after apply
  1265  			Src: cty.ObjectVal(map[string]cty.Value{
  1266  				"map": cty.MapVal(map[string]cty.Value{
  1267  					"a": cty.StringVal("a"),
  1268  					"b": cty.StringVal("b"),
  1269  				}),
  1270  			}),
  1271  			Dst: cty.ObjectVal(map[string]cty.Value{
  1272  				"map": cty.MapVal(map[string]cty.Value{
  1273  					"a": cty.StringVal("a"),
  1274  				}),
  1275  			}),
  1276  			Expect: cty.ObjectVal(map[string]cty.Value{
  1277  				"map": cty.MapVal(map[string]cty.Value{
  1278  					"a": cty.StringVal("a"),
  1279  				}),
  1280  			}),
  1281  			Apply: true,
  1282  		},
  1283  		{
  1284  			Src: cty.ObjectVal(map[string]cty.Value{
  1285  				"a": cty.StringVal("a"),
  1286  			}),
  1287  			Dst: cty.EmptyObjectVal,
  1288  			Expect: cty.ObjectVal(map[string]cty.Value{
  1289  				"a": cty.NullVal(cty.String),
  1290  			}),
  1291  		},
  1292  
  1293  		// a list in an object in a list, going from null to empty
  1294  		{
  1295  			Src: cty.ObjectVal(map[string]cty.Value{
  1296  				"network_interface": cty.ListVal([]cty.Value{
  1297  					cty.ObjectVal(map[string]cty.Value{
  1298  						"network_ip":    cty.UnknownVal(cty.String),
  1299  						"access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))),
  1300  						"address":       cty.NullVal(cty.String),
  1301  						"name":          cty.StringVal("nic0"),
  1302  					})}),
  1303  			}),
  1304  			Dst: cty.ObjectVal(map[string]cty.Value{
  1305  				"network_interface": cty.ListVal([]cty.Value{
  1306  					cty.ObjectVal(map[string]cty.Value{
  1307  						"network_ip":    cty.StringVal("10.128.0.64"),
  1308  						"access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})),
  1309  						"address":       cty.StringVal("address"),
  1310  						"name":          cty.StringVal("nic0"),
  1311  					}),
  1312  				}),
  1313  			}),
  1314  			Expect: cty.ObjectVal(map[string]cty.Value{
  1315  				"network_interface": cty.ListVal([]cty.Value{
  1316  					cty.ObjectVal(map[string]cty.Value{
  1317  						"network_ip":    cty.StringVal("10.128.0.64"),
  1318  						"access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))),
  1319  						"address":       cty.StringVal("address"),
  1320  						"name":          cty.StringVal("nic0"),
  1321  					}),
  1322  				}),
  1323  			}),
  1324  			Apply: true,
  1325  		},
  1326  
  1327  		// a list in an object in a list, going from empty to null
  1328  		{
  1329  			Src: cty.ObjectVal(map[string]cty.Value{
  1330  				"network_interface": cty.ListVal([]cty.Value{
  1331  					cty.ObjectVal(map[string]cty.Value{
  1332  						"network_ip":    cty.UnknownVal(cty.String),
  1333  						"access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})),
  1334  						"address":       cty.NullVal(cty.String),
  1335  						"name":          cty.StringVal("nic0"),
  1336  					})}),
  1337  			}),
  1338  			Dst: cty.ObjectVal(map[string]cty.Value{
  1339  				"network_interface": cty.ListVal([]cty.Value{
  1340  					cty.ObjectVal(map[string]cty.Value{
  1341  						"network_ip":    cty.StringVal("10.128.0.64"),
  1342  						"access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))),
  1343  						"address":       cty.StringVal("address"),
  1344  						"name":          cty.StringVal("nic0"),
  1345  					}),
  1346  				}),
  1347  			}),
  1348  			Expect: cty.ObjectVal(map[string]cty.Value{
  1349  				"network_interface": cty.ListVal([]cty.Value{
  1350  					cty.ObjectVal(map[string]cty.Value{
  1351  						"network_ip":    cty.StringVal("10.128.0.64"),
  1352  						"access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})),
  1353  						"address":       cty.StringVal("address"),
  1354  						"name":          cty.StringVal("nic0"),
  1355  					}),
  1356  				}),
  1357  			}),
  1358  			Apply: true,
  1359  		},
  1360  		// the empty list should be transferred, but the new unknown should not be overridden
  1361  		{
  1362  			Src: cty.ObjectVal(map[string]cty.Value{
  1363  				"network_interface": cty.ListVal([]cty.Value{
  1364  					cty.ObjectVal(map[string]cty.Value{
  1365  						"network_ip":    cty.StringVal("10.128.0.64"),
  1366  						"access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})),
  1367  						"address":       cty.NullVal(cty.String),
  1368  						"name":          cty.StringVal("nic0"),
  1369  					})}),
  1370  			}),
  1371  			Dst: cty.ObjectVal(map[string]cty.Value{
  1372  				"network_interface": cty.ListVal([]cty.Value{
  1373  					cty.ObjectVal(map[string]cty.Value{
  1374  						"network_ip":    cty.UnknownVal(cty.String),
  1375  						"access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))),
  1376  						"address":       cty.StringVal("address"),
  1377  						"name":          cty.StringVal("nic0"),
  1378  					}),
  1379  				}),
  1380  			}),
  1381  			Expect: cty.ObjectVal(map[string]cty.Value{
  1382  				"network_interface": cty.ListVal([]cty.Value{
  1383  					cty.ObjectVal(map[string]cty.Value{
  1384  						"network_ip":    cty.UnknownVal(cty.String),
  1385  						"access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})),
  1386  						"address":       cty.StringVal("address"),
  1387  						"name":          cty.StringVal("nic0"),
  1388  					}),
  1389  				}),
  1390  			}),
  1391  		},
  1392  		{
  1393  			// fix unknowns added to a map
  1394  			Src: cty.ObjectVal(map[string]cty.Value{
  1395  				"map": cty.MapVal(map[string]cty.Value{
  1396  					"a": cty.StringVal("a"),
  1397  					"b": cty.StringVal(""),
  1398  				}),
  1399  			}),
  1400  			Dst: cty.ObjectVal(map[string]cty.Value{
  1401  				"map": cty.MapVal(map[string]cty.Value{
  1402  					"a": cty.StringVal("a"),
  1403  					"b": cty.UnknownVal(cty.String),
  1404  				}),
  1405  			}),
  1406  			Expect: cty.ObjectVal(map[string]cty.Value{
  1407  				"map": cty.MapVal(map[string]cty.Value{
  1408  					"a": cty.StringVal("a"),
  1409  					"b": cty.StringVal(""),
  1410  				}),
  1411  			}),
  1412  		},
  1413  		{
  1414  			// fix unknowns lost from a list
  1415  			Src: cty.ObjectVal(map[string]cty.Value{
  1416  				"top": cty.ListVal([]cty.Value{
  1417  					cty.ObjectVal(map[string]cty.Value{
  1418  						"list": cty.ListVal([]cty.Value{
  1419  							cty.ObjectVal(map[string]cty.Value{
  1420  								"values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
  1421  							}),
  1422  						}),
  1423  					}),
  1424  				}),
  1425  			}),
  1426  			Dst: cty.ObjectVal(map[string]cty.Value{
  1427  				"top": cty.ListVal([]cty.Value{
  1428  					cty.ObjectVal(map[string]cty.Value{
  1429  						"list": cty.ListVal([]cty.Value{
  1430  							cty.ObjectVal(map[string]cty.Value{
  1431  								"values": cty.NullVal(cty.List(cty.String)),
  1432  							}),
  1433  						}),
  1434  					}),
  1435  				}),
  1436  			}),
  1437  			Expect: cty.ObjectVal(map[string]cty.Value{
  1438  				"top": cty.ListVal([]cty.Value{
  1439  					cty.ObjectVal(map[string]cty.Value{
  1440  						"list": cty.ListVal([]cty.Value{
  1441  							cty.ObjectVal(map[string]cty.Value{
  1442  								"values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}),
  1443  							}),
  1444  						}),
  1445  					}),
  1446  				}),
  1447  			}),
  1448  		},
  1449  		{
  1450  			Src: cty.ObjectVal(map[string]cty.Value{
  1451  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1452  					"list": cty.List(cty.String),
  1453  				}))),
  1454  			}),
  1455  			Dst: cty.ObjectVal(map[string]cty.Value{
  1456  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{
  1457  					"list": cty.List(cty.String),
  1458  				})),
  1459  			}),
  1460  			Expect: cty.ObjectVal(map[string]cty.Value{
  1461  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{
  1462  					"list": cty.List(cty.String),
  1463  				})),
  1464  			}),
  1465  		},
  1466  		{
  1467  			Src: cty.ObjectVal(map[string]cty.Value{
  1468  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1469  					"list": cty.List(cty.String),
  1470  				}))),
  1471  			}),
  1472  			Dst: cty.ObjectVal(map[string]cty.Value{
  1473  				"set": cty.SetValEmpty(cty.Object(map[string]cty.Type{
  1474  					"list": cty.List(cty.String),
  1475  				})),
  1476  			}),
  1477  			Expect: cty.ObjectVal(map[string]cty.Value{
  1478  				"set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{
  1479  					"list": cty.List(cty.String),
  1480  				}))),
  1481  			}),
  1482  			Apply: true,
  1483  		},
  1484  	} {
  1485  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
  1486  			got := normalizeNullValues(tc.Dst, tc.Src, tc.Apply)
  1487  			if !got.RawEquals(tc.Expect) {
  1488  				t.Fatalf("\nexpected: %#v\ngot:      %#v\n", tc.Expect, got)
  1489  			}
  1490  		})
  1491  	}
  1492  }
  1493  
  1494  func TestValidateNulls(t *testing.T) {
  1495  	for i, tc := range []struct {
  1496  		Cfg cty.Value
  1497  		Err bool
  1498  	}{
  1499  		{
  1500  			Cfg: cty.ObjectVal(map[string]cty.Value{
  1501  				"list": cty.ListVal([]cty.Value{
  1502  					cty.StringVal("string"),
  1503  					cty.NullVal(cty.String),
  1504  				}),
  1505  			}),
  1506  			Err: true,
  1507  		},
  1508  		{
  1509  			Cfg: cty.ObjectVal(map[string]cty.Value{
  1510  				"map": cty.MapVal(map[string]cty.Value{
  1511  					"string": cty.StringVal("string"),
  1512  					"null":   cty.NullVal(cty.String),
  1513  				}),
  1514  			}),
  1515  			Err: false,
  1516  		},
  1517  		{
  1518  			Cfg: cty.ObjectVal(map[string]cty.Value{
  1519  				"object": cty.ObjectVal(map[string]cty.Value{
  1520  					"list": cty.ListVal([]cty.Value{
  1521  						cty.StringVal("string"),
  1522  						cty.NullVal(cty.String),
  1523  					}),
  1524  				}),
  1525  			}),
  1526  			Err: true,
  1527  		},
  1528  		{
  1529  			Cfg: cty.ObjectVal(map[string]cty.Value{
  1530  				"object": cty.ObjectVal(map[string]cty.Value{
  1531  					"list": cty.ListVal([]cty.Value{
  1532  						cty.StringVal("string"),
  1533  						cty.NullVal(cty.String),
  1534  					}),
  1535  					"list2": cty.ListVal([]cty.Value{
  1536  						cty.StringVal("string"),
  1537  						cty.NullVal(cty.String),
  1538  					}),
  1539  				}),
  1540  			}),
  1541  			Err: true,
  1542  		},
  1543  		{
  1544  			Cfg: cty.ObjectVal(map[string]cty.Value{
  1545  				"object": cty.ObjectVal(map[string]cty.Value{
  1546  					"list": cty.SetVal([]cty.Value{
  1547  						cty.StringVal("string"),
  1548  						cty.NullVal(cty.String),
  1549  					}),
  1550  				}),
  1551  			}),
  1552  			Err: true,
  1553  		},
  1554  	} {
  1555  		t.Run(strconv.Itoa(i), func(t *testing.T) {
  1556  			d := validateConfigNulls(tc.Cfg, nil)
  1557  			diags := convert.ProtoToDiagnostics(d)
  1558  			switch {
  1559  			case tc.Err:
  1560  				if !diags.HasErrors() {
  1561  					t.Fatal("expected error")
  1562  				}
  1563  			default:
  1564  				if diags.HasErrors() {
  1565  					t.Fatalf("unexpected error: %q", diags.Err())
  1566  				}
  1567  			}
  1568  		})
  1569  	}
  1570  }