github.com/opentofu/opentofu@v1.7.1/internal/tofu/context_plan_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 tofu
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"os"
    13  	"reflect"
    14  	"sort"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"testing"
    19  
    20  	"github.com/davecgh/go-spew/spew"
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/zclconf/go-cty/cty"
    23  
    24  	"github.com/opentofu/opentofu/internal/addrs"
    25  	"github.com/opentofu/opentofu/internal/configs/configschema"
    26  	"github.com/opentofu/opentofu/internal/configs/hcl2shim"
    27  	"github.com/opentofu/opentofu/internal/lang/marks"
    28  	"github.com/opentofu/opentofu/internal/plans"
    29  	"github.com/opentofu/opentofu/internal/providers"
    30  	"github.com/opentofu/opentofu/internal/provisioners"
    31  	"github.com/opentofu/opentofu/internal/states"
    32  	"github.com/opentofu/opentofu/internal/tfdiags"
    33  )
    34  
    35  func TestContext2Plan_basic(t *testing.T) {
    36  	m := testModule(t, "plan-good")
    37  	p := testProvider("aws")
    38  	ctx := testContext2(t, &ContextOpts{
    39  		Providers: map[addrs.Provider]providers.Factory{
    40  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
    41  		},
    42  	})
    43  
    44  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
    45  	if diags.HasErrors() {
    46  		t.Fatalf("unexpected errors: %s", diags.Err())
    47  	}
    48  
    49  	if l := len(plan.Changes.Resources); l < 2 {
    50  		t.Fatalf("wrong number of resources %d; want fewer than two\n%s", l, spew.Sdump(plan.Changes.Resources))
    51  	}
    52  
    53  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
    54  	ty := schema.ImpliedType()
    55  	for _, r := range plan.Changes.Resources {
    56  		ric, err := r.Decode(ty)
    57  		if err != nil {
    58  			t.Fatal(err)
    59  		}
    60  
    61  		switch i := ric.Addr.String(); i {
    62  		case "aws_instance.bar":
    63  			foo := ric.After.GetAttr("foo").AsString()
    64  			if foo != "2" {
    65  				t.Fatalf("incorrect plan for 'bar': %#v", ric.After)
    66  			}
    67  		case "aws_instance.foo":
    68  			num, _ := ric.After.GetAttr("num").AsBigFloat().Int64()
    69  			if num != 2 {
    70  				t.Fatalf("incorrect plan for 'foo': %#v", ric.After)
    71  			}
    72  		default:
    73  			t.Fatal("unknown instance:", i)
    74  		}
    75  	}
    76  
    77  	if !p.ValidateProviderConfigCalled {
    78  		t.Fatal("provider config was not checked before Configure")
    79  	}
    80  
    81  }
    82  
    83  func TestContext2Plan_createBefore_deposed(t *testing.T) {
    84  	m := testModule(t, "plan-cbd")
    85  	p := testProvider("aws")
    86  	p.PlanResourceChangeFn = testDiffFn
    87  
    88  	state := states.NewState()
    89  	root := state.EnsureModule(addrs.RootModuleInstance)
    90  	root.SetResourceInstanceCurrent(
    91  		mustResourceInstanceAddr("aws_instance.foo").Resource,
    92  		&states.ResourceInstanceObjectSrc{
    93  			Status:    states.ObjectReady,
    94  			AttrsJSON: []byte(`{"id":"baz","type":"aws_instance"}`),
    95  		},
    96  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
    97  	)
    98  	root.SetResourceInstanceDeposed(
    99  		mustResourceInstanceAddr("aws_instance.foo").Resource,
   100  		states.DeposedKey("00000001"),
   101  		&states.ResourceInstanceObjectSrc{
   102  			Status:    states.ObjectReady,
   103  			AttrsJSON: []byte(`{"id":"foo"}`),
   104  		},
   105  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   106  	)
   107  
   108  	ctx := testContext2(t, &ContextOpts{
   109  		Providers: map[addrs.Provider]providers.Factory{
   110  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   111  		},
   112  	})
   113  
   114  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
   115  	if diags.HasErrors() {
   116  		t.Fatalf("unexpected errors: %s", diags.Err())
   117  	}
   118  
   119  	// the state should still show one deposed
   120  	expectedState := strings.TrimSpace(`
   121   aws_instance.foo: (1 deposed)
   122    ID = baz
   123    provider = provider["registry.opentofu.org/hashicorp/aws"]
   124    type = aws_instance
   125    Deposed ID 1 = foo`)
   126  
   127  	if plan.PriorState.String() != expectedState {
   128  		t.Fatalf("\nexpected: %q\ngot:      %q\n", expectedState, plan.PriorState.String())
   129  	}
   130  
   131  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   132  	ty := schema.ImpliedType()
   133  
   134  	type InstanceGen struct {
   135  		Addr       string
   136  		DeposedKey states.DeposedKey
   137  	}
   138  	want := map[InstanceGen]bool{
   139  		{
   140  			Addr: "aws_instance.foo",
   141  		}: true,
   142  		{
   143  			Addr:       "aws_instance.foo",
   144  			DeposedKey: states.DeposedKey("00000001"),
   145  		}: true,
   146  	}
   147  	got := make(map[InstanceGen]bool)
   148  	changes := make(map[InstanceGen]*plans.ResourceInstanceChangeSrc)
   149  
   150  	for _, change := range plan.Changes.Resources {
   151  		k := InstanceGen{
   152  			Addr:       change.Addr.String(),
   153  			DeposedKey: change.DeposedKey,
   154  		}
   155  		got[k] = true
   156  		changes[k] = change
   157  	}
   158  	if !reflect.DeepEqual(got, want) {
   159  		t.Fatalf("wrong resource instance object changes in plan\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(want))
   160  	}
   161  
   162  	{
   163  		ric, err := changes[InstanceGen{Addr: "aws_instance.foo"}].Decode(ty)
   164  		if err != nil {
   165  			t.Fatal(err)
   166  		}
   167  
   168  		if got, want := ric.Action, plans.NoOp; got != want {
   169  			t.Errorf("current object change action is %s; want %s", got, want)
   170  		}
   171  
   172  		// the existing instance should only have an unchanged id
   173  		expected, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   174  			"id":   cty.StringVal("baz"),
   175  			"type": cty.StringVal("aws_instance"),
   176  		}))
   177  		if err != nil {
   178  			t.Fatal(err)
   179  		}
   180  
   181  		checkVals(t, expected, ric.After)
   182  	}
   183  
   184  	{
   185  		ric, err := changes[InstanceGen{Addr: "aws_instance.foo", DeposedKey: states.DeposedKey("00000001")}].Decode(ty)
   186  		if err != nil {
   187  			t.Fatal(err)
   188  		}
   189  
   190  		if got, want := ric.Action, plans.Delete; got != want {
   191  			t.Errorf("deposed object change action is %s; want %s", got, want)
   192  		}
   193  	}
   194  }
   195  
   196  func TestContext2Plan_createBefore_maintainRoot(t *testing.T) {
   197  	m := testModule(t, "plan-cbd-maintain-root")
   198  	p := testProvider("aws")
   199  	ctx := testContext2(t, &ContextOpts{
   200  		Providers: map[addrs.Provider]providers.Factory{
   201  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   202  		},
   203  	})
   204  
   205  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   206  	if diags.HasErrors() {
   207  		t.Fatalf("unexpected errors: %s", diags.Err())
   208  	}
   209  
   210  	if !plan.PriorState.Empty() {
   211  		t.Fatal("expected empty prior state, got:", plan.PriorState)
   212  	}
   213  
   214  	if len(plan.Changes.Resources) != 4 {
   215  		t.Error("expected 4 resource in plan, got", len(plan.Changes.Resources))
   216  	}
   217  
   218  	for _, res := range plan.Changes.Resources {
   219  		// these should all be creates
   220  		if res.Action != plans.Create {
   221  			t.Fatalf("unexpected action %s for %s", res.Action, res.Addr.String())
   222  		}
   223  	}
   224  }
   225  
   226  func TestContext2Plan_emptyDiff(t *testing.T) {
   227  	m := testModule(t, "plan-empty")
   228  	p := testProvider("aws")
   229  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
   230  		resp.PlannedState = req.ProposedNewState
   231  		return resp
   232  	}
   233  
   234  	ctx := testContext2(t, &ContextOpts{
   235  		Providers: map[addrs.Provider]providers.Factory{
   236  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   237  		},
   238  	})
   239  
   240  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   241  	if diags.HasErrors() {
   242  		t.Fatalf("unexpected errors: %s", diags.Err())
   243  	}
   244  
   245  	if !plan.PriorState.Empty() {
   246  		t.Fatal("expected empty state, got:", plan.PriorState)
   247  	}
   248  
   249  	if len(plan.Changes.Resources) != 2 {
   250  		t.Error("expected 2 resource in plan, got", len(plan.Changes.Resources))
   251  	}
   252  
   253  	actions := map[string]plans.Action{}
   254  
   255  	for _, res := range plan.Changes.Resources {
   256  		actions[res.Addr.String()] = res.Action
   257  	}
   258  
   259  	expected := map[string]plans.Action{
   260  		"aws_instance.foo": plans.Create,
   261  		"aws_instance.bar": plans.Create,
   262  	}
   263  	if !cmp.Equal(expected, actions) {
   264  		t.Fatal(cmp.Diff(expected, actions))
   265  	}
   266  }
   267  
   268  func TestContext2Plan_escapedVar(t *testing.T) {
   269  	m := testModule(t, "plan-escaped-var")
   270  	p := testProvider("aws")
   271  	p.PlanResourceChangeFn = testDiffFn
   272  	ctx := testContext2(t, &ContextOpts{
   273  		Providers: map[addrs.Provider]providers.Factory{
   274  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   275  		},
   276  	})
   277  
   278  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   279  	if diags.HasErrors() {
   280  		t.Fatalf("unexpected errors: %s", diags.Err())
   281  	}
   282  
   283  	if len(plan.Changes.Resources) != 1 {
   284  		t.Error("expected 1 resource in plan, got", len(plan.Changes.Resources))
   285  	}
   286  
   287  	res := plan.Changes.Resources[0]
   288  	if res.Action != plans.Create {
   289  		t.Fatalf("expected resource creation, got %s", res.Action)
   290  	}
   291  
   292  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   293  	ty := schema.ImpliedType()
   294  
   295  	ric, err := res.Decode(ty)
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  
   300  	expected := objectVal(t, schema, map[string]cty.Value{
   301  		"id":   cty.UnknownVal(cty.String),
   302  		"foo":  cty.StringVal("bar-${baz}"),
   303  		"type": cty.UnknownVal(cty.String),
   304  	})
   305  
   306  	checkVals(t, expected, ric.After)
   307  }
   308  
   309  func TestContext2Plan_minimal(t *testing.T) {
   310  	m := testModule(t, "plan-empty")
   311  	p := testProvider("aws")
   312  	ctx := testContext2(t, &ContextOpts{
   313  		Providers: map[addrs.Provider]providers.Factory{
   314  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   315  		},
   316  	})
   317  
   318  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   319  	if diags.HasErrors() {
   320  		t.Fatalf("unexpected errors: %s", diags.Err())
   321  	}
   322  
   323  	if !plan.PriorState.Empty() {
   324  		t.Fatal("expected empty state, got:", plan.PriorState)
   325  	}
   326  
   327  	if len(plan.Changes.Resources) != 2 {
   328  		t.Error("expected 2 resource in plan, got", len(plan.Changes.Resources))
   329  	}
   330  
   331  	actions := map[string]plans.Action{}
   332  
   333  	for _, res := range plan.Changes.Resources {
   334  		actions[res.Addr.String()] = res.Action
   335  	}
   336  
   337  	expected := map[string]plans.Action{
   338  		"aws_instance.foo": plans.Create,
   339  		"aws_instance.bar": plans.Create,
   340  	}
   341  	if !cmp.Equal(expected, actions) {
   342  		t.Fatal(cmp.Diff(expected, actions))
   343  	}
   344  }
   345  
   346  func TestContext2Plan_modules(t *testing.T) {
   347  	m := testModule(t, "plan-modules")
   348  	p := testProvider("aws")
   349  	p.PlanResourceChangeFn = testDiffFn
   350  	ctx := testContext2(t, &ContextOpts{
   351  		Providers: map[addrs.Provider]providers.Factory{
   352  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   353  		},
   354  	})
   355  
   356  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   357  	if diags.HasErrors() {
   358  		t.Fatalf("unexpected errors: %s", diags.Err())
   359  	}
   360  
   361  	if len(plan.Changes.Resources) != 3 {
   362  		t.Error("expected 3 resource in plan, got", len(plan.Changes.Resources))
   363  	}
   364  
   365  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   366  	ty := schema.ImpliedType()
   367  
   368  	expectFoo := objectVal(t, schema, map[string]cty.Value{
   369  		"id":   cty.UnknownVal(cty.String),
   370  		"foo":  cty.StringVal("2"),
   371  		"type": cty.UnknownVal(cty.String),
   372  	})
   373  
   374  	expectNum := objectVal(t, schema, map[string]cty.Value{
   375  		"id":   cty.UnknownVal(cty.String),
   376  		"num":  cty.NumberIntVal(2),
   377  		"type": cty.UnknownVal(cty.String),
   378  	})
   379  
   380  	for _, res := range plan.Changes.Resources {
   381  		if res.Action != plans.Create {
   382  			t.Fatalf("expected resource creation, got %s", res.Action)
   383  		}
   384  		ric, err := res.Decode(ty)
   385  		if err != nil {
   386  			t.Fatal(err)
   387  		}
   388  
   389  		var expected cty.Value
   390  		switch i := ric.Addr.String(); i {
   391  		case "aws_instance.bar":
   392  			expected = expectFoo
   393  		case "aws_instance.foo":
   394  			expected = expectNum
   395  		case "module.child.aws_instance.foo":
   396  			expected = expectNum
   397  		default:
   398  			t.Fatal("unknown instance:", i)
   399  		}
   400  
   401  		checkVals(t, expected, ric.After)
   402  	}
   403  }
   404  func TestContext2Plan_moduleExpand(t *testing.T) {
   405  	// Test a smattering of plan expansion behavior
   406  	m := testModule(t, "plan-modules-expand")
   407  	p := testProvider("aws")
   408  	ctx := testContext2(t, &ContextOpts{
   409  		Providers: map[addrs.Provider]providers.Factory{
   410  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   411  		},
   412  	})
   413  
   414  	plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
   415  	if diags.HasErrors() {
   416  		t.Fatalf("unexpected errors: %s", diags.Err())
   417  	}
   418  
   419  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   420  	ty := schema.ImpliedType()
   421  
   422  	expected := map[string]struct{}{
   423  		`aws_instance.foo["a"]`:                          {},
   424  		`module.count_child[1].aws_instance.foo[0]`:      {},
   425  		`module.count_child[1].aws_instance.foo[1]`:      {},
   426  		`module.count_child[0].aws_instance.foo[0]`:      {},
   427  		`module.count_child[0].aws_instance.foo[1]`:      {},
   428  		`module.for_each_child["a"].aws_instance.foo[1]`: {},
   429  		`module.for_each_child["a"].aws_instance.foo[0]`: {},
   430  	}
   431  
   432  	for _, res := range plan.Changes.Resources {
   433  		if res.Action != plans.Create {
   434  			t.Fatalf("expected resource creation, got %s", res.Action)
   435  		}
   436  		ric, err := res.Decode(ty)
   437  		if err != nil {
   438  			t.Fatal(err)
   439  		}
   440  
   441  		_, ok := expected[ric.Addr.String()]
   442  		if !ok {
   443  			t.Fatal("unexpected resource:", ric.Addr.String())
   444  		}
   445  		delete(expected, ric.Addr.String())
   446  	}
   447  	for addr := range expected {
   448  		t.Error("missing resource", addr)
   449  	}
   450  }
   451  
   452  // GH-1475
   453  func TestContext2Plan_moduleCycle(t *testing.T) {
   454  	m := testModule(t, "plan-module-cycle")
   455  	p := testProvider("aws")
   456  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   457  		ResourceTypes: map[string]*configschema.Block{
   458  			"aws_instance": {
   459  				Attributes: map[string]*configschema.Attribute{
   460  					"id":         {Type: cty.String, Computed: true},
   461  					"some_input": {Type: cty.String, Optional: true},
   462  					"type":       {Type: cty.String, Computed: true},
   463  				},
   464  			},
   465  		},
   466  	})
   467  
   468  	ctx := testContext2(t, &ContextOpts{
   469  		Providers: map[addrs.Provider]providers.Factory{
   470  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   471  		},
   472  	})
   473  
   474  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   475  	if diags.HasErrors() {
   476  		t.Fatalf("unexpected errors: %s", diags.Err())
   477  	}
   478  
   479  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   480  	ty := schema.ImpliedType()
   481  
   482  	if len(plan.Changes.Resources) != 2 {
   483  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
   484  	}
   485  
   486  	for _, res := range plan.Changes.Resources {
   487  		if res.Action != plans.Create {
   488  			t.Fatalf("expected resource creation, got %s", res.Action)
   489  		}
   490  		ric, err := res.Decode(ty)
   491  		if err != nil {
   492  			t.Fatal(err)
   493  		}
   494  
   495  		var expected cty.Value
   496  		switch i := ric.Addr.String(); i {
   497  		case "aws_instance.b":
   498  			expected = objectVal(t, schema, map[string]cty.Value{
   499  				"id":   cty.UnknownVal(cty.String),
   500  				"type": cty.UnknownVal(cty.String),
   501  			})
   502  		case "aws_instance.c":
   503  			expected = objectVal(t, schema, map[string]cty.Value{
   504  				"id":         cty.UnknownVal(cty.String),
   505  				"some_input": cty.UnknownVal(cty.String),
   506  				"type":       cty.UnknownVal(cty.String),
   507  			})
   508  		default:
   509  			t.Fatal("unknown instance:", i)
   510  		}
   511  
   512  		checkVals(t, expected, ric.After)
   513  	}
   514  }
   515  
   516  func TestContext2Plan_moduleDeadlock(t *testing.T) {
   517  	testCheckDeadlock(t, func() {
   518  		m := testModule(t, "plan-module-deadlock")
   519  		p := testProvider("aws")
   520  		p.PlanResourceChangeFn = testDiffFn
   521  
   522  		ctx := testContext2(t, &ContextOpts{
   523  			Providers: map[addrs.Provider]providers.Factory{
   524  				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   525  			},
   526  		})
   527  
   528  		plan, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   529  		if err != nil {
   530  			t.Fatalf("err: %s", err)
   531  		}
   532  
   533  		schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   534  		ty := schema.ImpliedType()
   535  
   536  		for _, res := range plan.Changes.Resources {
   537  			if res.Action != plans.Create {
   538  				t.Fatalf("expected resource creation, got %s", res.Action)
   539  			}
   540  			ric, err := res.Decode(ty)
   541  			if err != nil {
   542  				t.Fatal(err)
   543  			}
   544  
   545  			expected := objectVal(t, schema, map[string]cty.Value{
   546  				"id":   cty.UnknownVal(cty.String),
   547  				"type": cty.UnknownVal(cty.String),
   548  			})
   549  			switch i := ric.Addr.String(); i {
   550  			case "module.child.aws_instance.foo[0]":
   551  			case "module.child.aws_instance.foo[1]":
   552  			case "module.child.aws_instance.foo[2]":
   553  			default:
   554  				t.Fatal("unknown instance:", i)
   555  			}
   556  
   557  			checkVals(t, expected, ric.After)
   558  		}
   559  	})
   560  }
   561  
   562  func TestContext2Plan_moduleInput(t *testing.T) {
   563  	m := testModule(t, "plan-module-input")
   564  	p := testProvider("aws")
   565  	p.PlanResourceChangeFn = testDiffFn
   566  	ctx := testContext2(t, &ContextOpts{
   567  		Providers: map[addrs.Provider]providers.Factory{
   568  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   569  		},
   570  	})
   571  
   572  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   573  	if diags.HasErrors() {
   574  		t.Fatalf("unexpected errors: %s", diags.Err())
   575  	}
   576  
   577  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   578  	ty := schema.ImpliedType()
   579  
   580  	if len(plan.Changes.Resources) != 2 {
   581  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
   582  	}
   583  
   584  	for _, res := range plan.Changes.Resources {
   585  		if res.Action != plans.Create {
   586  			t.Fatalf("expected resource creation, got %s", res.Action)
   587  		}
   588  		ric, err := res.Decode(ty)
   589  		if err != nil {
   590  			t.Fatal(err)
   591  		}
   592  
   593  		var expected cty.Value
   594  
   595  		switch i := ric.Addr.String(); i {
   596  		case "aws_instance.bar":
   597  			expected = objectVal(t, schema, map[string]cty.Value{
   598  				"id":   cty.UnknownVal(cty.String),
   599  				"foo":  cty.StringVal("2"),
   600  				"type": cty.UnknownVal(cty.String),
   601  			})
   602  		case "module.child.aws_instance.foo":
   603  			expected = objectVal(t, schema, map[string]cty.Value{
   604  				"id":   cty.UnknownVal(cty.String),
   605  				"foo":  cty.StringVal("42"),
   606  				"type": cty.UnknownVal(cty.String),
   607  			})
   608  		default:
   609  			t.Fatal("unknown instance:", i)
   610  		}
   611  
   612  		checkVals(t, expected, ric.After)
   613  	}
   614  }
   615  
   616  func TestContext2Plan_moduleInputComputed(t *testing.T) {
   617  	m := testModule(t, "plan-module-input-computed")
   618  	p := testProvider("aws")
   619  	p.PlanResourceChangeFn = testDiffFn
   620  	ctx := testContext2(t, &ContextOpts{
   621  		Providers: map[addrs.Provider]providers.Factory{
   622  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   623  		},
   624  	})
   625  
   626  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   627  	if diags.HasErrors() {
   628  		t.Fatalf("unexpected errors: %s", diags.Err())
   629  	}
   630  
   631  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   632  	ty := schema.ImpliedType()
   633  
   634  	if len(plan.Changes.Resources) != 2 {
   635  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
   636  	}
   637  
   638  	for _, res := range plan.Changes.Resources {
   639  		if res.Action != plans.Create {
   640  			t.Fatalf("expected resource creation, got %s", res.Action)
   641  		}
   642  		ric, err := res.Decode(ty)
   643  		if err != nil {
   644  			t.Fatal(err)
   645  		}
   646  
   647  		switch i := ric.Addr.String(); i {
   648  		case "aws_instance.bar":
   649  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   650  				"id":      cty.UnknownVal(cty.String),
   651  				"foo":     cty.UnknownVal(cty.String),
   652  				"type":    cty.UnknownVal(cty.String),
   653  				"compute": cty.StringVal("foo"),
   654  			}), ric.After)
   655  		case "module.child.aws_instance.foo":
   656  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   657  				"id":   cty.UnknownVal(cty.String),
   658  				"foo":  cty.UnknownVal(cty.String),
   659  				"type": cty.UnknownVal(cty.String),
   660  			}), ric.After)
   661  		default:
   662  			t.Fatal("unknown instance:", i)
   663  		}
   664  	}
   665  }
   666  
   667  func TestContext2Plan_moduleInputFromVar(t *testing.T) {
   668  	m := testModule(t, "plan-module-input-var")
   669  	p := testProvider("aws")
   670  	p.PlanResourceChangeFn = testDiffFn
   671  	ctx := testContext2(t, &ContextOpts{
   672  		Providers: map[addrs.Provider]providers.Factory{
   673  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   674  		},
   675  	})
   676  
   677  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
   678  		Mode: plans.NormalMode,
   679  		SetVariables: InputValues{
   680  			"foo": &InputValue{
   681  				Value:      cty.StringVal("52"),
   682  				SourceType: ValueFromCaller,
   683  			},
   684  		},
   685  	})
   686  	if diags.HasErrors() {
   687  		t.Fatalf("unexpected errors: %s", diags.Err())
   688  	}
   689  
   690  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   691  	ty := schema.ImpliedType()
   692  
   693  	if len(plan.Changes.Resources) != 2 {
   694  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
   695  	}
   696  
   697  	for _, res := range plan.Changes.Resources {
   698  		if res.Action != plans.Create {
   699  			t.Fatalf("expected resource creation, got %s", res.Action)
   700  		}
   701  		ric, err := res.Decode(ty)
   702  		if err != nil {
   703  			t.Fatal(err)
   704  		}
   705  
   706  		switch i := ric.Addr.String(); i {
   707  		case "aws_instance.bar":
   708  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   709  				"id":   cty.UnknownVal(cty.String),
   710  				"foo":  cty.StringVal("2"),
   711  				"type": cty.UnknownVal(cty.String),
   712  			}), ric.After)
   713  		case "module.child.aws_instance.foo":
   714  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   715  				"id":   cty.UnknownVal(cty.String),
   716  				"foo":  cty.StringVal("52"),
   717  				"type": cty.UnknownVal(cty.String),
   718  			}), ric.After)
   719  		default:
   720  			t.Fatal("unknown instance:", i)
   721  		}
   722  	}
   723  }
   724  
   725  func TestContext2Plan_moduleMultiVar(t *testing.T) {
   726  	m := testModule(t, "plan-module-multi-var")
   727  	p := testProvider("aws")
   728  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   729  		ResourceTypes: map[string]*configschema.Block{
   730  			"aws_instance": {
   731  				Attributes: map[string]*configschema.Attribute{
   732  					"id":  {Type: cty.String, Computed: true},
   733  					"foo": {Type: cty.String, Optional: true},
   734  					"baz": {Type: cty.String, Optional: true},
   735  				},
   736  			},
   737  		},
   738  	})
   739  
   740  	ctx := testContext2(t, &ContextOpts{
   741  		Providers: map[addrs.Provider]providers.Factory{
   742  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   743  		},
   744  	})
   745  
   746  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
   747  	if diags.HasErrors() {
   748  		t.Fatalf("unexpected errors: %s", diags.Err())
   749  	}
   750  
   751  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   752  	ty := schema.ImpliedType()
   753  
   754  	if len(plan.Changes.Resources) != 5 {
   755  		t.Fatal("expected 5 changes, got", len(plan.Changes.Resources))
   756  	}
   757  
   758  	for _, res := range plan.Changes.Resources {
   759  		if res.Action != plans.Create {
   760  			t.Fatalf("expected resource creation, got %s", res.Action)
   761  		}
   762  
   763  		ric, err := res.Decode(ty)
   764  		if err != nil {
   765  			t.Fatal(err)
   766  		}
   767  
   768  		switch i := ric.Addr.String(); i {
   769  		case "aws_instance.parent[0]":
   770  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   771  				"id": cty.UnknownVal(cty.String),
   772  			}), ric.After)
   773  		case "aws_instance.parent[1]":
   774  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   775  				"id": cty.UnknownVal(cty.String),
   776  			}), ric.After)
   777  		case "module.child.aws_instance.bar[0]":
   778  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   779  				"id":  cty.UnknownVal(cty.String),
   780  				"baz": cty.StringVal("baz"),
   781  			}), ric.After)
   782  		case "module.child.aws_instance.bar[1]":
   783  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   784  				"id":  cty.UnknownVal(cty.String),
   785  				"baz": cty.StringVal("baz"),
   786  			}), ric.After)
   787  		case "module.child.aws_instance.foo":
   788  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   789  				"id":  cty.UnknownVal(cty.String),
   790  				"foo": cty.StringVal("baz,baz"),
   791  			}), ric.After)
   792  		default:
   793  			t.Fatal("unknown instance:", i)
   794  		}
   795  	}
   796  }
   797  
   798  func TestContext2Plan_moduleOrphans(t *testing.T) {
   799  	m := testModule(t, "plan-modules-remove")
   800  	p := testProvider("aws")
   801  	p.PlanResourceChangeFn = testDiffFn
   802  
   803  	state := states.NewState()
   804  	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
   805  	child.SetResourceInstanceCurrent(
   806  		mustResourceInstanceAddr("aws_instance.foo").Resource,
   807  		&states.ResourceInstanceObjectSrc{
   808  			Status:    states.ObjectReady,
   809  			AttrsJSON: []byte(`{"id":"baz"}`),
   810  		},
   811  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   812  	)
   813  
   814  	ctx := testContext2(t, &ContextOpts{
   815  		Providers: map[addrs.Provider]providers.Factory{
   816  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   817  		},
   818  	})
   819  
   820  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
   821  	if diags.HasErrors() {
   822  		t.Fatalf("unexpected errors: %s", diags.Err())
   823  	}
   824  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   825  	ty := schema.ImpliedType()
   826  
   827  	if len(plan.Changes.Resources) != 2 {
   828  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
   829  	}
   830  
   831  	for _, res := range plan.Changes.Resources {
   832  
   833  		ric, err := res.Decode(ty)
   834  		if err != nil {
   835  			t.Fatal(err)
   836  		}
   837  
   838  		switch i := ric.Addr.String(); i {
   839  		case "aws_instance.foo":
   840  			if res.Action != plans.Create {
   841  				t.Fatalf("expected resource creation, got %s", res.Action)
   842  			}
   843  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
   844  				"id":   cty.UnknownVal(cty.String),
   845  				"num":  cty.NumberIntVal(2),
   846  				"type": cty.UnknownVal(cty.String),
   847  			}), ric.After)
   848  		case "module.child.aws_instance.foo":
   849  			if res.Action != plans.Delete {
   850  				t.Fatalf("expected resource delete, got %s", res.Action)
   851  			}
   852  		default:
   853  			t.Fatal("unknown instance:", i)
   854  		}
   855  	}
   856  
   857  	expectedState := `<no state>
   858  module.child:
   859    aws_instance.foo:
   860      ID = baz
   861      provider = provider["registry.opentofu.org/hashicorp/aws"]`
   862  
   863  	if plan.PriorState.String() != expectedState {
   864  		t.Fatalf("\nexpected state: %q\n\ngot: %q", expectedState, plan.PriorState.String())
   865  	}
   866  }
   867  
   868  // https://github.com/hashicorp/terraform/issues/3114
   869  func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) {
   870  	m := testModule(t, "plan-modules-remove-provisioners")
   871  	p := testProvider("aws")
   872  	p.PlanResourceChangeFn = testDiffFn
   873  	pr := testProvisioner()
   874  
   875  	state := states.NewState()
   876  	root := state.EnsureModule(addrs.RootModuleInstance)
   877  	root.SetResourceInstanceCurrent(
   878  		mustResourceInstanceAddr("aws_instance.top").Resource,
   879  		&states.ResourceInstanceObjectSrc{
   880  			Status:    states.ObjectReady,
   881  			AttrsJSON: []byte(`{"id":"top","type":"aws_instance"}`),
   882  		},
   883  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   884  	)
   885  	child1 := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey).Child("child1", addrs.NoKey))
   886  	child1.SetResourceInstanceCurrent(
   887  		mustResourceInstanceAddr("aws_instance.foo").Resource,
   888  		&states.ResourceInstanceObjectSrc{
   889  			Status:    states.ObjectReady,
   890  			AttrsJSON: []byte(`{"id":"baz","type":"aws_instance"}`),
   891  		},
   892  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   893  	)
   894  	child2 := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey).Child("child2", addrs.NoKey))
   895  	child2.SetResourceInstanceCurrent(
   896  		mustResourceInstanceAddr("aws_instance.foo").Resource,
   897  		&states.ResourceInstanceObjectSrc{
   898  			Status:    states.ObjectReady,
   899  			AttrsJSON: []byte(`{"id":"baz","type":"aws_instance"}`),
   900  		},
   901  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
   902  	)
   903  
   904  	ctx := testContext2(t, &ContextOpts{
   905  		Providers: map[addrs.Provider]providers.Factory{
   906  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
   907  		},
   908  		Provisioners: map[string]provisioners.Factory{
   909  			"shell": testProvisionerFuncFixed(pr),
   910  		},
   911  	})
   912  
   913  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
   914  	if diags.HasErrors() {
   915  		t.Fatalf("unexpected errors: %s", diags.Err())
   916  	}
   917  
   918  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
   919  	ty := schema.ImpliedType()
   920  
   921  	if len(plan.Changes.Resources) != 3 {
   922  		t.Error("expected 3 planned resources, got", len(plan.Changes.Resources))
   923  	}
   924  
   925  	for _, res := range plan.Changes.Resources {
   926  
   927  		ric, err := res.Decode(ty)
   928  		if err != nil {
   929  			t.Fatal(err)
   930  		}
   931  
   932  		switch i := ric.Addr.String(); i {
   933  		case "module.parent.module.child1.aws_instance.foo":
   934  			if res.Action != plans.Delete {
   935  				t.Fatalf("expected resource Delete, got %s", res.Action)
   936  			}
   937  		case "module.parent.module.child2.aws_instance.foo":
   938  			if res.Action != plans.Delete {
   939  				t.Fatalf("expected resource Delete, got %s", res.Action)
   940  			}
   941  		case "aws_instance.top":
   942  			if res.Action != plans.NoOp {
   943  				t.Fatalf("expected no changes, got %s", res.Action)
   944  			}
   945  		default:
   946  			t.Fatalf("unknown instance: %s\nafter: %#v", i, hcl2shim.ConfigValueFromHCL2(ric.After))
   947  		}
   948  	}
   949  
   950  	expectedState := `aws_instance.top:
   951    ID = top
   952    provider = provider["registry.opentofu.org/hashicorp/aws"]
   953    type = aws_instance
   954  
   955  module.parent.child1:
   956    aws_instance.foo:
   957      ID = baz
   958      provider = provider["registry.opentofu.org/hashicorp/aws"]
   959      type = aws_instance
   960  module.parent.child2:
   961    aws_instance.foo:
   962      ID = baz
   963      provider = provider["registry.opentofu.org/hashicorp/aws"]
   964      type = aws_instance`
   965  
   966  	if expectedState != plan.PriorState.String() {
   967  		t.Fatalf("\nexpect state:\n%s\n\ngot state:\n%s\n", expectedState, plan.PriorState.String())
   968  	}
   969  }
   970  
   971  func TestContext2Plan_moduleProviderInherit(t *testing.T) {
   972  	var l sync.Mutex
   973  	var calls []string
   974  
   975  	m := testModule(t, "plan-module-provider-inherit")
   976  	ctx := testContext2(t, &ContextOpts{
   977  		Providers: map[addrs.Provider]providers.Factory{
   978  			addrs.NewDefaultProvider("aws"): func() (providers.Interface, error) {
   979  				l.Lock()
   980  				defer l.Unlock()
   981  
   982  				p := testProvider("aws")
   983  				p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
   984  					Provider: &configschema.Block{
   985  						Attributes: map[string]*configschema.Attribute{
   986  							"from": {Type: cty.String, Optional: true},
   987  						},
   988  					},
   989  					ResourceTypes: map[string]*configschema.Block{
   990  						"aws_instance": {
   991  							Attributes: map[string]*configschema.Attribute{
   992  								"from": {Type: cty.String, Optional: true},
   993  							},
   994  						},
   995  					},
   996  				})
   997  				p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   998  					from := req.Config.GetAttr("from")
   999  					if from.IsNull() || from.AsString() != "root" {
  1000  						resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not root"))
  1001  					}
  1002  
  1003  					return
  1004  				}
  1005  				p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  1006  					from := req.Config.GetAttr("from").AsString()
  1007  
  1008  					l.Lock()
  1009  					defer l.Unlock()
  1010  					calls = append(calls, from)
  1011  					return testDiffFn(req)
  1012  				}
  1013  				return p, nil
  1014  			},
  1015  		},
  1016  	})
  1017  
  1018  	_, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1019  	if err != nil {
  1020  		t.Fatalf("err: %s", err)
  1021  	}
  1022  
  1023  	actual := calls
  1024  	sort.Strings(actual)
  1025  	expected := []string{"child", "root"}
  1026  	if !reflect.DeepEqual(actual, expected) {
  1027  		t.Fatalf("bad: %#v", actual)
  1028  	}
  1029  }
  1030  
  1031  // This tests (for GH-11282) that deeply nested modules properly inherit
  1032  // configuration.
  1033  func TestContext2Plan_moduleProviderInheritDeep(t *testing.T) {
  1034  	var l sync.Mutex
  1035  
  1036  	m := testModule(t, "plan-module-provider-inherit-deep")
  1037  	ctx := testContext2(t, &ContextOpts{
  1038  		Providers: map[addrs.Provider]providers.Factory{
  1039  			addrs.NewDefaultProvider("aws"): func() (providers.Interface, error) {
  1040  				l.Lock()
  1041  				defer l.Unlock()
  1042  
  1043  				var from string
  1044  				p := testProvider("aws")
  1045  
  1046  				p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1047  					Provider: &configschema.Block{
  1048  						Attributes: map[string]*configschema.Attribute{
  1049  							"from": {Type: cty.String, Optional: true},
  1050  						},
  1051  					},
  1052  					ResourceTypes: map[string]*configschema.Block{
  1053  						"aws_instance": {
  1054  							Attributes: map[string]*configschema.Attribute{},
  1055  						},
  1056  					},
  1057  				})
  1058  
  1059  				p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
  1060  					v := req.Config.GetAttr("from")
  1061  					if v.IsNull() || v.AsString() != "root" {
  1062  						resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not root"))
  1063  					}
  1064  					from = v.AsString()
  1065  
  1066  					return
  1067  				}
  1068  
  1069  				p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  1070  					if from != "root" {
  1071  						resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("bad resource"))
  1072  						return
  1073  					}
  1074  
  1075  					return testDiffFn(req)
  1076  				}
  1077  				return p, nil
  1078  			},
  1079  		},
  1080  	})
  1081  
  1082  	_, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1083  	if err != nil {
  1084  		t.Fatalf("err: %s", err)
  1085  	}
  1086  }
  1087  
  1088  func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) {
  1089  	var l sync.Mutex
  1090  	var calls []string
  1091  
  1092  	m := testModule(t, "plan-module-provider-defaults-var")
  1093  	ctx := testContext2(t, &ContextOpts{
  1094  		Providers: map[addrs.Provider]providers.Factory{
  1095  			addrs.NewDefaultProvider("aws"): func() (providers.Interface, error) {
  1096  				l.Lock()
  1097  				defer l.Unlock()
  1098  
  1099  				p := testProvider("aws")
  1100  				p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1101  					Provider: &configschema.Block{
  1102  						Attributes: map[string]*configschema.Attribute{
  1103  							"to":   {Type: cty.String, Optional: true},
  1104  							"from": {Type: cty.String, Optional: true},
  1105  						},
  1106  					},
  1107  					ResourceTypes: map[string]*configschema.Block{
  1108  						"aws_instance": {
  1109  							Attributes: map[string]*configschema.Attribute{
  1110  								"from": {Type: cty.String, Optional: true},
  1111  							},
  1112  						},
  1113  					},
  1114  				})
  1115  				p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
  1116  					var buf bytes.Buffer
  1117  					from := req.Config.GetAttr("from")
  1118  					if !from.IsNull() {
  1119  						buf.WriteString(from.AsString() + "\n")
  1120  					}
  1121  					to := req.Config.GetAttr("to")
  1122  					if !to.IsNull() {
  1123  						buf.WriteString(to.AsString() + "\n")
  1124  					}
  1125  
  1126  					l.Lock()
  1127  					defer l.Unlock()
  1128  					calls = append(calls, buf.String())
  1129  					return
  1130  				}
  1131  
  1132  				return p, nil
  1133  			},
  1134  		},
  1135  	})
  1136  
  1137  	_, err := ctx.Plan(m, states.NewState(), &PlanOpts{
  1138  		Mode: plans.NormalMode,
  1139  		SetVariables: InputValues{
  1140  			"foo": &InputValue{
  1141  				Value:      cty.StringVal("root"),
  1142  				SourceType: ValueFromCaller,
  1143  			},
  1144  		},
  1145  	})
  1146  	if err != nil {
  1147  		t.Fatalf("err: %s", err)
  1148  	}
  1149  
  1150  	expected := []string{
  1151  		"child\nchild\n",
  1152  		"root\n",
  1153  	}
  1154  	sort.Strings(calls)
  1155  	if !reflect.DeepEqual(calls, expected) {
  1156  		t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, calls)
  1157  	}
  1158  }
  1159  
  1160  func TestContext2Plan_moduleProviderVar(t *testing.T) {
  1161  	m := testModule(t, "plan-module-provider-var")
  1162  	p := testProvider("aws")
  1163  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1164  		Provider: &configschema.Block{
  1165  			Attributes: map[string]*configschema.Attribute{
  1166  				"value": {Type: cty.String, Optional: true},
  1167  			},
  1168  		},
  1169  		ResourceTypes: map[string]*configschema.Block{
  1170  			"aws_instance": {
  1171  				Attributes: map[string]*configschema.Attribute{
  1172  					"value": {Type: cty.String, Optional: true},
  1173  				},
  1174  			},
  1175  		},
  1176  	})
  1177  
  1178  	ctx := testContext2(t, &ContextOpts{
  1179  		Providers: map[addrs.Provider]providers.Factory{
  1180  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1181  		},
  1182  	})
  1183  
  1184  	plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  1185  	if diags.HasErrors() {
  1186  		t.Fatalf("unexpected errors: %s", diags.Err())
  1187  	}
  1188  
  1189  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  1190  	ty := schema.ImpliedType()
  1191  
  1192  	if len(plan.Changes.Resources) != 1 {
  1193  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  1194  	}
  1195  
  1196  	for _, res := range plan.Changes.Resources {
  1197  		if res.Action != plans.Create {
  1198  			t.Fatalf("expected resource creation, got %s", res.Action)
  1199  		}
  1200  		ric, err := res.Decode(ty)
  1201  		if err != nil {
  1202  			t.Fatal(err)
  1203  		}
  1204  
  1205  		switch i := ric.Addr.String(); i {
  1206  		case "module.child.aws_instance.test":
  1207  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  1208  				"value": cty.StringVal("hello"),
  1209  			}), ric.After)
  1210  		default:
  1211  			t.Fatal("unknown instance:", i)
  1212  		}
  1213  	}
  1214  }
  1215  
  1216  func TestContext2Plan_moduleVar(t *testing.T) {
  1217  	m := testModule(t, "plan-module-var")
  1218  	p := testProvider("aws")
  1219  	p.PlanResourceChangeFn = testDiffFn
  1220  	ctx := testContext2(t, &ContextOpts{
  1221  		Providers: map[addrs.Provider]providers.Factory{
  1222  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1223  		},
  1224  	})
  1225  
  1226  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1227  	if diags.HasErrors() {
  1228  		t.Fatalf("unexpected errors: %s", diags.Err())
  1229  	}
  1230  
  1231  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  1232  	ty := schema.ImpliedType()
  1233  
  1234  	if len(plan.Changes.Resources) != 2 {
  1235  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  1236  	}
  1237  
  1238  	for _, res := range plan.Changes.Resources {
  1239  		if res.Action != plans.Create {
  1240  			t.Fatalf("expected resource creation, got %s", res.Action)
  1241  		}
  1242  		ric, err := res.Decode(ty)
  1243  		if err != nil {
  1244  			t.Fatal(err)
  1245  		}
  1246  
  1247  		var expected cty.Value
  1248  
  1249  		switch i := ric.Addr.String(); i {
  1250  		case "aws_instance.bar":
  1251  			expected = objectVal(t, schema, map[string]cty.Value{
  1252  				"id":   cty.UnknownVal(cty.String),
  1253  				"foo":  cty.StringVal("2"),
  1254  				"type": cty.UnknownVal(cty.String),
  1255  			})
  1256  		case "module.child.aws_instance.foo":
  1257  			expected = objectVal(t, schema, map[string]cty.Value{
  1258  				"id":   cty.UnknownVal(cty.String),
  1259  				"num":  cty.NumberIntVal(2),
  1260  				"type": cty.UnknownVal(cty.String),
  1261  			})
  1262  		default:
  1263  			t.Fatal("unknown instance:", i)
  1264  		}
  1265  
  1266  		checkVals(t, expected, ric.After)
  1267  	}
  1268  }
  1269  
  1270  func TestContext2Plan_moduleVarWrongTypeBasic(t *testing.T) {
  1271  	m := testModule(t, "plan-module-wrong-var-type")
  1272  	p := testProvider("aws")
  1273  	ctx := testContext2(t, &ContextOpts{
  1274  		Providers: map[addrs.Provider]providers.Factory{
  1275  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1276  		},
  1277  	})
  1278  
  1279  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1280  	if !diags.HasErrors() {
  1281  		t.Fatalf("succeeded; want errors")
  1282  	}
  1283  }
  1284  
  1285  func TestContext2Plan_moduleVarWrongTypeNested(t *testing.T) {
  1286  	m := testModule(t, "plan-module-wrong-var-type-nested")
  1287  	p := testProvider("null")
  1288  	ctx := testContext2(t, &ContextOpts{
  1289  		Providers: map[addrs.Provider]providers.Factory{
  1290  			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
  1291  		},
  1292  	})
  1293  
  1294  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1295  	if !diags.HasErrors() {
  1296  		t.Fatalf("succeeded; want errors")
  1297  	}
  1298  }
  1299  
  1300  func TestContext2Plan_moduleVarWithDefaultValue(t *testing.T) {
  1301  	m := testModule(t, "plan-module-var-with-default-value")
  1302  	p := testProvider("null")
  1303  	ctx := testContext2(t, &ContextOpts{
  1304  		Providers: map[addrs.Provider]providers.Factory{
  1305  			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
  1306  		},
  1307  	})
  1308  
  1309  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1310  	if diags.HasErrors() {
  1311  		t.Fatalf("unexpected errors: %s", diags.Err())
  1312  	}
  1313  }
  1314  
  1315  func TestContext2Plan_moduleVarComputed(t *testing.T) {
  1316  	m := testModule(t, "plan-module-var-computed")
  1317  	p := testProvider("aws")
  1318  	p.PlanResourceChangeFn = testDiffFn
  1319  	ctx := testContext2(t, &ContextOpts{
  1320  		Providers: map[addrs.Provider]providers.Factory{
  1321  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1322  		},
  1323  	})
  1324  
  1325  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1326  	if diags.HasErrors() {
  1327  		t.Fatalf("unexpected errors: %s", diags.Err())
  1328  	}
  1329  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  1330  	ty := schema.ImpliedType()
  1331  
  1332  	if len(plan.Changes.Resources) != 2 {
  1333  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  1334  	}
  1335  
  1336  	for _, res := range plan.Changes.Resources {
  1337  		if res.Action != plans.Create {
  1338  			t.Fatalf("expected resource creation, got %s", res.Action)
  1339  		}
  1340  		ric, err := res.Decode(ty)
  1341  		if err != nil {
  1342  			t.Fatal(err)
  1343  		}
  1344  
  1345  		switch i := ric.Addr.String(); i {
  1346  		case "aws_instance.bar":
  1347  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  1348  				"id":   cty.UnknownVal(cty.String),
  1349  				"foo":  cty.UnknownVal(cty.String),
  1350  				"type": cty.UnknownVal(cty.String),
  1351  			}), ric.After)
  1352  		case "module.child.aws_instance.foo":
  1353  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  1354  				"id":      cty.UnknownVal(cty.String),
  1355  				"foo":     cty.UnknownVal(cty.String),
  1356  				"type":    cty.UnknownVal(cty.String),
  1357  				"compute": cty.StringVal("foo"),
  1358  			}), ric.After)
  1359  		default:
  1360  			t.Fatal("unknown instance:", i)
  1361  		}
  1362  	}
  1363  }
  1364  
  1365  func TestContext2Plan_preventDestroy_bad(t *testing.T) {
  1366  	m := testModule(t, "plan-prevent-destroy-bad")
  1367  	p := testProvider("aws")
  1368  	p.PlanResourceChangeFn = testDiffFn
  1369  	state := states.NewState()
  1370  	root := state.EnsureModule(addrs.RootModuleInstance)
  1371  	root.SetResourceInstanceCurrent(
  1372  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  1373  		&states.ResourceInstanceObjectSrc{
  1374  			Status:    states.ObjectReady,
  1375  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  1376  		},
  1377  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1378  	)
  1379  
  1380  	ctx := testContext2(t, &ContextOpts{
  1381  		Providers: map[addrs.Provider]providers.Factory{
  1382  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1383  		},
  1384  	})
  1385  
  1386  	plan, err := ctx.Plan(m, state, DefaultPlanOpts)
  1387  
  1388  	expectedErr := "aws_instance.foo has lifecycle.prevent_destroy"
  1389  	if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) {
  1390  		if plan != nil {
  1391  			t.Logf(legacyDiffComparisonString(plan.Changes))
  1392  		}
  1393  		t.Fatalf("expected err would contain %q\nerr: %s", expectedErr, err)
  1394  	}
  1395  
  1396  	// Plan should show the expected changes, even though prevent_destroy validation fails
  1397  	// So we could see why the resource was attempted to be destroyed
  1398  	if got, want := 1, len(plan.Changes.Resources); got != want {
  1399  		t.Fatalf("wrong number of planned resource changes %d; want %d\n%s", got, want, spew.Sdump(plan.Changes.Resources))
  1400  	}
  1401  }
  1402  
  1403  func TestContext2Plan_preventDestroy_good(t *testing.T) {
  1404  	m := testModule(t, "plan-prevent-destroy-good")
  1405  	p := testProvider("aws")
  1406  	p.PlanResourceChangeFn = testDiffFn
  1407  
  1408  	state := states.NewState()
  1409  	root := state.EnsureModule(addrs.RootModuleInstance)
  1410  	root.SetResourceInstanceCurrent(
  1411  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  1412  		&states.ResourceInstanceObjectSrc{
  1413  			Status:    states.ObjectReady,
  1414  			AttrsJSON: []byte(`{"id":"i-abc123","type":"aws_instance"}`),
  1415  		},
  1416  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1417  	)
  1418  
  1419  	ctx := testContext2(t, &ContextOpts{
  1420  		Providers: map[addrs.Provider]providers.Factory{
  1421  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1422  		},
  1423  	})
  1424  
  1425  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  1426  	if diags.HasErrors() {
  1427  		t.Fatalf("unexpected errors: %s", diags.Err())
  1428  	}
  1429  
  1430  	if !plan.Changes.Empty() {
  1431  		t.Fatalf("expected no changes, got %#v\n", plan.Changes)
  1432  	}
  1433  }
  1434  
  1435  func TestContext2Plan_preventDestroy_countBad(t *testing.T) {
  1436  	m := testModule(t, "plan-prevent-destroy-count-bad")
  1437  	p := testProvider("aws")
  1438  
  1439  	state := states.NewState()
  1440  	root := state.EnsureModule(addrs.RootModuleInstance)
  1441  	root.SetResourceInstanceCurrent(
  1442  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  1443  		&states.ResourceInstanceObjectSrc{
  1444  			Status:    states.ObjectReady,
  1445  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  1446  		},
  1447  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1448  	)
  1449  	root.SetResourceInstanceCurrent(
  1450  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  1451  		&states.ResourceInstanceObjectSrc{
  1452  			Status:    states.ObjectReady,
  1453  			AttrsJSON: []byte(`{"id":"i-abc345"}`),
  1454  		},
  1455  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1456  	)
  1457  
  1458  	ctx := testContext2(t, &ContextOpts{
  1459  		Providers: map[addrs.Provider]providers.Factory{
  1460  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1461  		},
  1462  	})
  1463  
  1464  	plan, err := ctx.Plan(m, state, DefaultPlanOpts)
  1465  
  1466  	expectedErr := "aws_instance.foo[1] has lifecycle.prevent_destroy"
  1467  	if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) {
  1468  		if plan != nil {
  1469  			t.Logf(legacyDiffComparisonString(plan.Changes))
  1470  		}
  1471  		t.Fatalf("expected err would contain %q\nerr: %s", expectedErr, err)
  1472  	}
  1473  
  1474  	// Plan should show the expected changes, even though prevent_destroy validation fails
  1475  	// So we could see why the resource was attempted to be destroyed
  1476  	if got, want := 1, len(plan.Changes.Resources); got != want {
  1477  		t.Fatalf("wrong number of planned resource changes %d; want %d\n%s", got, want, spew.Sdump(plan.Changes.Resources))
  1478  	}
  1479  }
  1480  
  1481  func TestContext2Plan_preventDestroy_countGood(t *testing.T) {
  1482  	m := testModule(t, "plan-prevent-destroy-count-good")
  1483  	p := testProvider("aws")
  1484  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1485  		ResourceTypes: map[string]*configschema.Block{
  1486  			"aws_instance": {
  1487  				Attributes: map[string]*configschema.Attribute{
  1488  					"current": {Type: cty.String, Optional: true},
  1489  					"id":      {Type: cty.String, Computed: true},
  1490  				},
  1491  			},
  1492  		},
  1493  	})
  1494  
  1495  	state := states.NewState()
  1496  	root := state.EnsureModule(addrs.RootModuleInstance)
  1497  	root.SetResourceInstanceCurrent(
  1498  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  1499  		&states.ResourceInstanceObjectSrc{
  1500  			Status:    states.ObjectReady,
  1501  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  1502  		},
  1503  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1504  	)
  1505  	root.SetResourceInstanceCurrent(
  1506  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  1507  		&states.ResourceInstanceObjectSrc{
  1508  			Status:    states.ObjectReady,
  1509  			AttrsJSON: []byte(`{"id":"i-abc345"}`),
  1510  		},
  1511  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1512  	)
  1513  
  1514  	ctx := testContext2(t, &ContextOpts{
  1515  		Providers: map[addrs.Provider]providers.Factory{
  1516  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1517  		},
  1518  	})
  1519  
  1520  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  1521  	if diags.HasErrors() {
  1522  		t.Fatalf("unexpected errors: %s", diags.Err())
  1523  	}
  1524  
  1525  	if plan.Changes.Empty() {
  1526  		t.Fatalf("Expected non-empty plan, got %s", legacyDiffComparisonString(plan.Changes))
  1527  	}
  1528  }
  1529  
  1530  func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) {
  1531  	m := testModule(t, "plan-prevent-destroy-count-good")
  1532  	p := testProvider("aws")
  1533  	p.PlanResourceChangeFn = testDiffFn
  1534  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1535  		ResourceTypes: map[string]*configschema.Block{
  1536  			"aws_instance": {
  1537  				Attributes: map[string]*configschema.Attribute{
  1538  					"current": {Type: cty.String, Optional: true},
  1539  					"type":    {Type: cty.String, Optional: true, Computed: true},
  1540  					"id":      {Type: cty.String, Computed: true},
  1541  				},
  1542  			},
  1543  		},
  1544  	})
  1545  
  1546  	state := states.NewState()
  1547  	root := state.EnsureModule(addrs.RootModuleInstance)
  1548  	root.SetResourceInstanceCurrent(
  1549  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  1550  		&states.ResourceInstanceObjectSrc{
  1551  			Status:    states.ObjectReady,
  1552  			AttrsJSON: []byte(`{"id":"i-abc123","current":"0","type":"aws_instance"}`),
  1553  		},
  1554  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1555  	)
  1556  
  1557  	ctx := testContext2(t, &ContextOpts{
  1558  		Providers: map[addrs.Provider]providers.Factory{
  1559  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1560  		},
  1561  	})
  1562  
  1563  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  1564  	if diags.HasErrors() {
  1565  		t.Fatalf("unexpected errors: %s", diags.Err())
  1566  	}
  1567  
  1568  	if !plan.Changes.Empty() {
  1569  		t.Fatalf("Expected empty plan, got %s", legacyDiffComparisonString(plan.Changes))
  1570  	}
  1571  }
  1572  
  1573  func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) {
  1574  	m := testModule(t, "plan-prevent-destroy-good")
  1575  	p := testProvider("aws")
  1576  
  1577  	state := states.NewState()
  1578  	root := state.EnsureModule(addrs.RootModuleInstance)
  1579  	root.SetResourceInstanceCurrent(
  1580  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  1581  		&states.ResourceInstanceObjectSrc{
  1582  			Status:    states.ObjectReady,
  1583  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  1584  		},
  1585  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1586  	)
  1587  
  1588  	ctx := testContext2(t, &ContextOpts{
  1589  		Providers: map[addrs.Provider]providers.Factory{
  1590  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1591  		},
  1592  	})
  1593  
  1594  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  1595  		Mode: plans.DestroyMode,
  1596  	})
  1597  
  1598  	expectedErr := "aws_instance.foo has lifecycle.prevent_destroy"
  1599  	if !strings.Contains(fmt.Sprintf("%s", diags.Err()), expectedErr) {
  1600  		if plan != nil {
  1601  			t.Logf(legacyDiffComparisonString(plan.Changes))
  1602  		}
  1603  		t.Fatalf("expected diagnostics would contain %q\nactual diags: %s", expectedErr, diags.Err())
  1604  	}
  1605  }
  1606  
  1607  func TestContext2Plan_provisionerCycle(t *testing.T) {
  1608  	m := testModule(t, "plan-provisioner-cycle")
  1609  	p := testProvider("aws")
  1610  	pr := testProvisioner()
  1611  	ctx := testContext2(t, &ContextOpts{
  1612  		Providers: map[addrs.Provider]providers.Factory{
  1613  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1614  		},
  1615  		Provisioners: map[string]provisioners.Factory{
  1616  			"local-exec": testProvisionerFuncFixed(pr),
  1617  		},
  1618  	})
  1619  
  1620  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1621  	if !diags.HasErrors() {
  1622  		t.Fatalf("succeeded; want errors")
  1623  	}
  1624  }
  1625  
  1626  func TestContext2Plan_computed(t *testing.T) {
  1627  	m := testModule(t, "plan-computed")
  1628  	p := testProvider("aws")
  1629  	p.PlanResourceChangeFn = testDiffFn
  1630  	ctx := testContext2(t, &ContextOpts{
  1631  		Providers: map[addrs.Provider]providers.Factory{
  1632  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1633  		},
  1634  	})
  1635  
  1636  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1637  	if diags.HasErrors() {
  1638  		t.Fatalf("unexpected errors: %s", diags.Err())
  1639  	}
  1640  
  1641  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  1642  	ty := schema.ImpliedType()
  1643  
  1644  	if len(plan.Changes.Resources) != 2 {
  1645  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  1646  	}
  1647  
  1648  	for _, res := range plan.Changes.Resources {
  1649  		if res.Action != plans.Create {
  1650  			t.Fatalf("expected resource creation, got %s", res.Action)
  1651  		}
  1652  		ric, err := res.Decode(ty)
  1653  		if err != nil {
  1654  			t.Fatal(err)
  1655  		}
  1656  
  1657  		switch i := ric.Addr.String(); i {
  1658  		case "aws_instance.bar":
  1659  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  1660  				"id":   cty.UnknownVal(cty.String),
  1661  				"foo":  cty.UnknownVal(cty.String),
  1662  				"type": cty.UnknownVal(cty.String),
  1663  			}), ric.After)
  1664  		case "aws_instance.foo":
  1665  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  1666  				"id":      cty.UnknownVal(cty.String),
  1667  				"foo":     cty.UnknownVal(cty.String),
  1668  				"num":     cty.NumberIntVal(2),
  1669  				"type":    cty.UnknownVal(cty.String),
  1670  				"compute": cty.StringVal("foo"),
  1671  			}), ric.After)
  1672  		default:
  1673  			t.Fatal("unknown instance:", i)
  1674  		}
  1675  	}
  1676  }
  1677  
  1678  func TestContext2Plan_blockNestingGroup(t *testing.T) {
  1679  	m := testModule(t, "plan-block-nesting-group")
  1680  	p := testProvider("test")
  1681  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1682  		ResourceTypes: map[string]*configschema.Block{
  1683  			"test": {
  1684  				BlockTypes: map[string]*configschema.NestedBlock{
  1685  					"blah": {
  1686  						Nesting: configschema.NestingGroup,
  1687  						Block: configschema.Block{
  1688  							Attributes: map[string]*configschema.Attribute{
  1689  								"baz": {Type: cty.String, Required: true},
  1690  							},
  1691  						},
  1692  					},
  1693  				},
  1694  			},
  1695  		},
  1696  	})
  1697  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1698  		return providers.PlanResourceChangeResponse{
  1699  			PlannedState: req.ProposedNewState,
  1700  		}
  1701  	}
  1702  	ctx := testContext2(t, &ContextOpts{
  1703  		Providers: map[addrs.Provider]providers.Factory{
  1704  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  1705  		},
  1706  	})
  1707  
  1708  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1709  	if diags.HasErrors() {
  1710  		t.Fatalf("unexpected errors: %s", diags.Err())
  1711  	}
  1712  
  1713  	if got, want := 1, len(plan.Changes.Resources); got != want {
  1714  		t.Fatalf("wrong number of planned resource changes %d; want %d\n%s", got, want, spew.Sdump(plan.Changes.Resources))
  1715  	}
  1716  
  1717  	if !p.PlanResourceChangeCalled {
  1718  		t.Fatalf("PlanResourceChange was not called at all")
  1719  	}
  1720  
  1721  	got := p.PlanResourceChangeRequest
  1722  	want := providers.PlanResourceChangeRequest{
  1723  		TypeName: "test",
  1724  
  1725  		// Because block type "blah" is defined as NestingGroup, we get a non-null
  1726  		// value for it with null nested attributes, rather than the "blah" object
  1727  		// itself being null, when there's no "blah" block in the config at all.
  1728  		//
  1729  		// This represents the situation where the remote service _always_ creates
  1730  		// a single "blah", regardless of whether the block is present, but when
  1731  		// the block _is_ present the user can override some aspects of it. The
  1732  		// absense of the block means "use the defaults", in that case.
  1733  		Config: cty.ObjectVal(map[string]cty.Value{
  1734  			"blah": cty.ObjectVal(map[string]cty.Value{
  1735  				"baz": cty.NullVal(cty.String),
  1736  			}),
  1737  		}),
  1738  		ProposedNewState: cty.ObjectVal(map[string]cty.Value{
  1739  			"blah": cty.ObjectVal(map[string]cty.Value{
  1740  				"baz": cty.NullVal(cty.String),
  1741  			}),
  1742  		}),
  1743  	}
  1744  	if !cmp.Equal(got, want, valueTrans) {
  1745  		t.Errorf("wrong PlanResourceChange request\n%s", cmp.Diff(got, want, valueTrans))
  1746  	}
  1747  }
  1748  
  1749  func TestContext2Plan_computedDataResource(t *testing.T) {
  1750  	m := testModule(t, "plan-computed-data-resource")
  1751  	p := testProvider("aws")
  1752  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1753  		ResourceTypes: map[string]*configschema.Block{
  1754  			"aws_instance": {
  1755  				Attributes: map[string]*configschema.Attribute{
  1756  					"num":     {Type: cty.String, Optional: true},
  1757  					"compute": {Type: cty.String, Optional: true},
  1758  					"foo":     {Type: cty.String, Computed: true},
  1759  				},
  1760  			},
  1761  		},
  1762  		DataSources: map[string]*configschema.Block{
  1763  			"aws_vpc": {
  1764  				Attributes: map[string]*configschema.Attribute{
  1765  					"foo": {Type: cty.String, Optional: true},
  1766  				},
  1767  			},
  1768  		},
  1769  	})
  1770  
  1771  	ctx := testContext2(t, &ContextOpts{
  1772  		Providers: map[addrs.Provider]providers.Factory{
  1773  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1774  		},
  1775  	})
  1776  
  1777  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1778  	if diags.HasErrors() {
  1779  		t.Fatalf("unexpected errors: %s", diags.Err())
  1780  	}
  1781  	schema := p.GetProviderSchemaResponse.DataSources["aws_vpc"].Block
  1782  	ty := schema.ImpliedType()
  1783  
  1784  	if rc := plan.Changes.ResourceInstance(addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "aws_instance", Name: "foo"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)); rc == nil {
  1785  		t.Fatalf("missing diff for aws_instance.foo")
  1786  	}
  1787  	rcs := plan.Changes.ResourceInstance(addrs.Resource{
  1788  		Mode: addrs.DataResourceMode,
  1789  		Type: "aws_vpc",
  1790  		Name: "bar",
  1791  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
  1792  	if rcs == nil {
  1793  		t.Fatalf("missing diff for data.aws_vpc.bar")
  1794  	}
  1795  
  1796  	rc, err := rcs.Decode(ty)
  1797  	if err != nil {
  1798  		t.Fatal(err)
  1799  	}
  1800  
  1801  	checkVals(t,
  1802  		cty.ObjectVal(map[string]cty.Value{
  1803  			"foo": cty.UnknownVal(cty.String),
  1804  		}),
  1805  		rc.After,
  1806  	)
  1807  	if got, want := rc.ActionReason, plans.ResourceInstanceReadBecauseConfigUnknown; got != want {
  1808  		t.Errorf("wrong ActionReason\ngot:  %s\nwant: %s", got, want)
  1809  	}
  1810  }
  1811  
  1812  func TestContext2Plan_computedInFunction(t *testing.T) {
  1813  	m := testModule(t, "plan-computed-in-function")
  1814  	p := testProvider("aws")
  1815  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1816  		ResourceTypes: map[string]*configschema.Block{
  1817  			"aws_instance": {
  1818  				Attributes: map[string]*configschema.Attribute{
  1819  					"attr": {Type: cty.Number, Optional: true},
  1820  				},
  1821  			},
  1822  		},
  1823  		DataSources: map[string]*configschema.Block{
  1824  			"aws_data_source": {
  1825  				Attributes: map[string]*configschema.Attribute{
  1826  					"computed": {Type: cty.List(cty.String), Computed: true},
  1827  				},
  1828  			},
  1829  		},
  1830  	})
  1831  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  1832  		State: cty.ObjectVal(map[string]cty.Value{
  1833  			"computed": cty.ListVal([]cty.Value{
  1834  				cty.StringVal("foo"),
  1835  			}),
  1836  		}),
  1837  	}
  1838  
  1839  	ctx := testContext2(t, &ContextOpts{
  1840  		Providers: map[addrs.Provider]providers.Factory{
  1841  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1842  		},
  1843  	})
  1844  
  1845  	diags := ctx.Validate(m)
  1846  	assertNoErrors(t, diags)
  1847  
  1848  	_, diags = ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1849  	assertNoErrors(t, diags)
  1850  
  1851  	if !p.ReadDataSourceCalled {
  1852  		t.Fatalf("ReadDataSource was not called on provider during plan; should've been called")
  1853  	}
  1854  }
  1855  
  1856  func TestContext2Plan_computedDataCountResource(t *testing.T) {
  1857  	m := testModule(t, "plan-computed-data-count")
  1858  	p := testProvider("aws")
  1859  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1860  		ResourceTypes: map[string]*configschema.Block{
  1861  			"aws_instance": {
  1862  				Attributes: map[string]*configschema.Attribute{
  1863  					"num":     {Type: cty.String, Optional: true},
  1864  					"compute": {Type: cty.String, Optional: true},
  1865  					"foo":     {Type: cty.String, Computed: true},
  1866  				},
  1867  			},
  1868  		},
  1869  		DataSources: map[string]*configschema.Block{
  1870  			"aws_vpc": {
  1871  				Attributes: map[string]*configschema.Attribute{
  1872  					"foo": {Type: cty.String, Optional: true},
  1873  				},
  1874  			},
  1875  		},
  1876  	})
  1877  
  1878  	ctx := testContext2(t, &ContextOpts{
  1879  		Providers: map[addrs.Provider]providers.Factory{
  1880  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1881  		},
  1882  	})
  1883  
  1884  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1885  	if diags.HasErrors() {
  1886  		t.Fatalf("unexpected errors: %s", diags.Err())
  1887  	}
  1888  
  1889  	// make sure we created 3 "bar"s
  1890  	for i := 0; i < 3; i++ {
  1891  		addr := addrs.Resource{
  1892  			Mode: addrs.DataResourceMode,
  1893  			Type: "aws_vpc",
  1894  			Name: "bar",
  1895  		}.Instance(addrs.IntKey(i)).Absolute(addrs.RootModuleInstance)
  1896  
  1897  		if rcs := plan.Changes.ResourceInstance(addr); rcs == nil {
  1898  			t.Fatalf("missing changes for %s", addr)
  1899  		}
  1900  	}
  1901  }
  1902  
  1903  func TestContext2Plan_localValueCount(t *testing.T) {
  1904  	m := testModule(t, "plan-local-value-count")
  1905  	p := testProvider("test")
  1906  	ctx := testContext2(t, &ContextOpts{
  1907  		Providers: map[addrs.Provider]providers.Factory{
  1908  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  1909  		},
  1910  	})
  1911  
  1912  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  1913  	if diags.HasErrors() {
  1914  		t.Fatalf("unexpected errors: %s", diags.Err())
  1915  	}
  1916  
  1917  	// make sure we created 3 "foo"s
  1918  	for i := 0; i < 3; i++ {
  1919  		addr := addrs.Resource{
  1920  			Mode: addrs.ManagedResourceMode,
  1921  			Type: "test_resource",
  1922  			Name: "foo",
  1923  		}.Instance(addrs.IntKey(i)).Absolute(addrs.RootModuleInstance)
  1924  
  1925  		if rcs := plan.Changes.ResourceInstance(addr); rcs == nil {
  1926  			t.Fatalf("missing changes for %s", addr)
  1927  		}
  1928  	}
  1929  }
  1930  
  1931  func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) {
  1932  	m := testModule(t, "plan-data-resource-becomes-computed")
  1933  	p := testProvider("aws")
  1934  
  1935  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  1936  		ResourceTypes: map[string]*configschema.Block{
  1937  			"aws_instance": {
  1938  				Attributes: map[string]*configschema.Attribute{
  1939  					"foo":      {Type: cty.String, Optional: true},
  1940  					"computed": {Type: cty.String, Computed: true},
  1941  				},
  1942  			},
  1943  		},
  1944  		DataSources: map[string]*configschema.Block{
  1945  			"aws_data_source": {
  1946  				Attributes: map[string]*configschema.Attribute{
  1947  					"id":  {Type: cty.String, Computed: true},
  1948  					"foo": {Type: cty.String, Optional: true},
  1949  				},
  1950  			},
  1951  		},
  1952  	})
  1953  
  1954  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  1955  		fooVal := req.ProposedNewState.GetAttr("foo")
  1956  		return providers.PlanResourceChangeResponse{
  1957  			PlannedState: cty.ObjectVal(map[string]cty.Value{
  1958  				"foo":      fooVal,
  1959  				"computed": cty.UnknownVal(cty.String),
  1960  			}),
  1961  			PlannedPrivate: req.PriorPrivate,
  1962  		}
  1963  	}
  1964  
  1965  	schema := p.GetProviderSchemaResponse.DataSources["aws_data_source"].Block
  1966  	ty := schema.ImpliedType()
  1967  
  1968  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  1969  		// This should not be called, because the configuration for the
  1970  		// data resource contains an unknown value for "foo".
  1971  		Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("ReadDataSource called, but should not have been")),
  1972  	}
  1973  
  1974  	state := states.NewState()
  1975  	root := state.EnsureModule(addrs.RootModuleInstance)
  1976  	root.SetResourceInstanceCurrent(
  1977  		mustResourceInstanceAddr("data.aws_data_source.foo").Resource,
  1978  		&states.ResourceInstanceObjectSrc{
  1979  			Status:    states.ObjectReady,
  1980  			AttrsJSON: []byte(`{"id":"i-abc123","foo":"baz"}`),
  1981  		},
  1982  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  1983  	)
  1984  
  1985  	ctx := testContext2(t, &ContextOpts{
  1986  		Providers: map[addrs.Provider]providers.Factory{
  1987  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  1988  		},
  1989  	})
  1990  
  1991  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  1992  	if diags.HasErrors() {
  1993  		t.Fatalf("unexpected errors during plan: %s", diags.Err())
  1994  	}
  1995  
  1996  	rcs := plan.Changes.ResourceInstance(addrs.Resource{
  1997  		Mode: addrs.DataResourceMode,
  1998  		Type: "aws_data_source",
  1999  		Name: "foo",
  2000  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
  2001  	if rcs == nil {
  2002  		t.Logf("full changeset: %s", spew.Sdump(plan.Changes))
  2003  		t.Fatalf("missing diff for data.aws_data_resource.foo")
  2004  	}
  2005  
  2006  	rc, err := rcs.Decode(ty)
  2007  	if err != nil {
  2008  		t.Fatal(err)
  2009  	}
  2010  
  2011  	if got, want := rc.ActionReason, plans.ResourceInstanceReadBecauseConfigUnknown; got != want {
  2012  		t.Errorf("wrong ActionReason\ngot:  %s\nwant: %s", got, want)
  2013  	}
  2014  
  2015  	// foo should now be unknown
  2016  	foo := rc.After.GetAttr("foo")
  2017  	if foo.IsKnown() {
  2018  		t.Fatalf("foo should be unknown, got %#v", foo)
  2019  	}
  2020  }
  2021  
  2022  func TestContext2Plan_computedList(t *testing.T) {
  2023  	m := testModule(t, "plan-computed-list")
  2024  	p := testProvider("aws")
  2025  	p.PlanResourceChangeFn = testDiffFn
  2026  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  2027  		ResourceTypes: map[string]*configschema.Block{
  2028  			"aws_instance": {
  2029  				Attributes: map[string]*configschema.Attribute{
  2030  					"compute": {Type: cty.String, Optional: true},
  2031  					"foo":     {Type: cty.String, Optional: true},
  2032  					"num":     {Type: cty.String, Optional: true},
  2033  					"list":    {Type: cty.List(cty.String), Computed: true},
  2034  				},
  2035  			},
  2036  		},
  2037  	})
  2038  
  2039  	ctx := testContext2(t, &ContextOpts{
  2040  		Providers: map[addrs.Provider]providers.Factory{
  2041  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2042  		},
  2043  	})
  2044  
  2045  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2046  	if diags.HasErrors() {
  2047  		t.Fatalf("unexpected errors: %s", diags.Err())
  2048  	}
  2049  
  2050  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2051  	ty := schema.ImpliedType()
  2052  
  2053  	if len(plan.Changes.Resources) != 2 {
  2054  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  2055  	}
  2056  
  2057  	for _, res := range plan.Changes.Resources {
  2058  		if res.Action != plans.Create {
  2059  			t.Fatalf("expected resource creation, got %s", res.Action)
  2060  		}
  2061  		ric, err := res.Decode(ty)
  2062  		if err != nil {
  2063  			t.Fatal(err)
  2064  		}
  2065  
  2066  		switch i := ric.Addr.String(); i {
  2067  		case "aws_instance.bar":
  2068  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2069  				"foo": cty.UnknownVal(cty.String),
  2070  			}), ric.After)
  2071  		case "aws_instance.foo":
  2072  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2073  				"list":    cty.UnknownVal(cty.List(cty.String)),
  2074  				"num":     cty.NumberIntVal(2),
  2075  				"compute": cty.StringVal("list.#"),
  2076  			}), ric.After)
  2077  		default:
  2078  			t.Fatal("unknown instance:", i)
  2079  		}
  2080  	}
  2081  }
  2082  
  2083  // GH-8695. This tests that you can index into a computed list on a
  2084  // splatted resource.
  2085  func TestContext2Plan_computedMultiIndex(t *testing.T) {
  2086  	m := testModule(t, "plan-computed-multi-index")
  2087  	p := testProvider("aws")
  2088  	p.PlanResourceChangeFn = testDiffFn
  2089  
  2090  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  2091  		ResourceTypes: map[string]*configschema.Block{
  2092  			"aws_instance": {
  2093  				Attributes: map[string]*configschema.Attribute{
  2094  					"compute": {Type: cty.String, Optional: true},
  2095  					"foo":     {Type: cty.List(cty.String), Optional: true},
  2096  					"ip":      {Type: cty.List(cty.String), Computed: true},
  2097  				},
  2098  			},
  2099  		},
  2100  	})
  2101  
  2102  	ctx := testContext2(t, &ContextOpts{
  2103  		Providers: map[addrs.Provider]providers.Factory{
  2104  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2105  		},
  2106  	})
  2107  
  2108  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2109  	if diags.HasErrors() {
  2110  		t.Fatalf("unexpected errors: %s", diags.Err())
  2111  	}
  2112  
  2113  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2114  	ty := schema.ImpliedType()
  2115  
  2116  	if len(plan.Changes.Resources) != 3 {
  2117  		t.Fatal("expected 3 changes, got", len(plan.Changes.Resources))
  2118  	}
  2119  
  2120  	for _, res := range plan.Changes.Resources {
  2121  		if res.Action != plans.Create {
  2122  			t.Fatalf("expected resource creation, got %s", res.Action)
  2123  		}
  2124  		ric, err := res.Decode(ty)
  2125  		if err != nil {
  2126  			t.Fatal(err)
  2127  		}
  2128  
  2129  		switch i := ric.Addr.String(); i {
  2130  		case "aws_instance.foo[0]":
  2131  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2132  				"ip":      cty.UnknownVal(cty.List(cty.String)),
  2133  				"foo":     cty.NullVal(cty.List(cty.String)),
  2134  				"compute": cty.StringVal("ip.#"),
  2135  			}), ric.After)
  2136  		case "aws_instance.foo[1]":
  2137  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2138  				"ip":      cty.UnknownVal(cty.List(cty.String)),
  2139  				"foo":     cty.NullVal(cty.List(cty.String)),
  2140  				"compute": cty.StringVal("ip.#"),
  2141  			}), ric.After)
  2142  		case "aws_instance.bar[0]":
  2143  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2144  				"foo": cty.UnknownVal(cty.List(cty.String)),
  2145  			}), ric.After)
  2146  		default:
  2147  			t.Fatal("unknown instance:", i)
  2148  		}
  2149  	}
  2150  }
  2151  
  2152  func TestContext2Plan_count(t *testing.T) {
  2153  	m := testModule(t, "plan-count")
  2154  	p := testProvider("aws")
  2155  	p.PlanResourceChangeFn = testDiffFn
  2156  	ctx := testContext2(t, &ContextOpts{
  2157  		Providers: map[addrs.Provider]providers.Factory{
  2158  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2159  		},
  2160  	})
  2161  
  2162  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2163  	if diags.HasErrors() {
  2164  		t.Fatalf("unexpected errors: %s", diags.Err())
  2165  	}
  2166  
  2167  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2168  	ty := schema.ImpliedType()
  2169  
  2170  	if len(plan.Changes.Resources) != 6 {
  2171  		t.Fatal("expected 6 changes, got", len(plan.Changes.Resources))
  2172  	}
  2173  
  2174  	for _, res := range plan.Changes.Resources {
  2175  		if res.Action != plans.Create {
  2176  			t.Fatalf("expected resource creation, got %s", res.Action)
  2177  		}
  2178  		ric, err := res.Decode(ty)
  2179  		if err != nil {
  2180  			t.Fatal(err)
  2181  		}
  2182  
  2183  		switch i := ric.Addr.String(); i {
  2184  		case "aws_instance.bar":
  2185  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2186  				"id":   cty.UnknownVal(cty.String),
  2187  				"foo":  cty.StringVal("foo,foo,foo,foo,foo"),
  2188  				"type": cty.UnknownVal(cty.String),
  2189  			}), ric.After)
  2190  		case "aws_instance.foo[0]":
  2191  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2192  				"id":   cty.UnknownVal(cty.String),
  2193  				"foo":  cty.StringVal("foo"),
  2194  				"type": cty.UnknownVal(cty.String),
  2195  			}), ric.After)
  2196  		case "aws_instance.foo[1]":
  2197  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2198  				"id":   cty.UnknownVal(cty.String),
  2199  				"foo":  cty.StringVal("foo"),
  2200  				"type": cty.UnknownVal(cty.String),
  2201  			}), ric.After)
  2202  		case "aws_instance.foo[2]":
  2203  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2204  				"id":   cty.UnknownVal(cty.String),
  2205  				"foo":  cty.StringVal("foo"),
  2206  				"type": cty.UnknownVal(cty.String),
  2207  			}), ric.After)
  2208  		case "aws_instance.foo[3]":
  2209  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2210  				"id":   cty.UnknownVal(cty.String),
  2211  				"foo":  cty.StringVal("foo"),
  2212  				"type": cty.UnknownVal(cty.String),
  2213  			}), ric.After)
  2214  		case "aws_instance.foo[4]":
  2215  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2216  				"id":   cty.UnknownVal(cty.String),
  2217  				"foo":  cty.StringVal("foo"),
  2218  				"type": cty.UnknownVal(cty.String),
  2219  			}), ric.After)
  2220  		default:
  2221  			t.Fatal("unknown instance:", i)
  2222  		}
  2223  	}
  2224  }
  2225  
  2226  func TestContext2Plan_countComputed(t *testing.T) {
  2227  	m := testModule(t, "plan-count-computed")
  2228  	p := testProvider("aws")
  2229  	ctx := testContext2(t, &ContextOpts{
  2230  		Providers: map[addrs.Provider]providers.Factory{
  2231  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2232  		},
  2233  	})
  2234  
  2235  	_, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2236  	if err == nil {
  2237  		t.Fatal("should error")
  2238  	}
  2239  }
  2240  
  2241  func TestContext2Plan_countComputedModule(t *testing.T) {
  2242  	m := testModule(t, "plan-count-computed-module")
  2243  	p := testProvider("aws")
  2244  	p.PlanResourceChangeFn = testDiffFn
  2245  	ctx := testContext2(t, &ContextOpts{
  2246  		Providers: map[addrs.Provider]providers.Factory{
  2247  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2248  		},
  2249  	})
  2250  
  2251  	_, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2252  
  2253  	expectedErr := `The "count" value depends on resource attributes`
  2254  	if !strings.Contains(fmt.Sprintf("%s", err), expectedErr) {
  2255  		t.Fatalf("expected err would contain %q\nerr: %s\n",
  2256  			expectedErr, err)
  2257  	}
  2258  }
  2259  
  2260  func TestContext2Plan_countModuleStatic(t *testing.T) {
  2261  	m := testModule(t, "plan-count-module-static")
  2262  	p := testProvider("aws")
  2263  	p.PlanResourceChangeFn = testDiffFn
  2264  	ctx := testContext2(t, &ContextOpts{
  2265  		Providers: map[addrs.Provider]providers.Factory{
  2266  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2267  		},
  2268  	})
  2269  
  2270  	plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  2271  	if diags.HasErrors() {
  2272  		t.Fatalf("unexpected errors: %s", diags.Err())
  2273  	}
  2274  
  2275  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2276  	ty := schema.ImpliedType()
  2277  
  2278  	if len(plan.Changes.Resources) != 3 {
  2279  		t.Fatal("expected 3 changes, got", len(plan.Changes.Resources))
  2280  	}
  2281  
  2282  	for _, res := range plan.Changes.Resources {
  2283  		if res.Action != plans.Create {
  2284  			t.Fatalf("expected resource creation, got %s", res.Action)
  2285  		}
  2286  		ric, err := res.Decode(ty)
  2287  		if err != nil {
  2288  			t.Fatal(err)
  2289  		}
  2290  
  2291  		switch i := ric.Addr.String(); i {
  2292  		case "module.child.aws_instance.foo[0]":
  2293  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2294  				"id":   cty.UnknownVal(cty.String),
  2295  				"type": cty.UnknownVal(cty.String),
  2296  			}), ric.After)
  2297  		case "module.child.aws_instance.foo[1]":
  2298  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2299  				"id":   cty.UnknownVal(cty.String),
  2300  				"type": cty.UnknownVal(cty.String),
  2301  			}), ric.After)
  2302  		case "module.child.aws_instance.foo[2]":
  2303  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2304  				"id":   cty.UnknownVal(cty.String),
  2305  				"type": cty.UnknownVal(cty.String),
  2306  			}), ric.After)
  2307  		default:
  2308  			t.Fatal("unknown instance:", i)
  2309  		}
  2310  	}
  2311  }
  2312  
  2313  func TestContext2Plan_countModuleStaticGrandchild(t *testing.T) {
  2314  	m := testModule(t, "plan-count-module-static-grandchild")
  2315  	p := testProvider("aws")
  2316  	p.PlanResourceChangeFn = testDiffFn
  2317  	ctx := testContext2(t, &ContextOpts{
  2318  		Providers: map[addrs.Provider]providers.Factory{
  2319  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2320  		},
  2321  	})
  2322  
  2323  	plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  2324  	if diags.HasErrors() {
  2325  		t.Fatalf("unexpected errors: %s", diags.Err())
  2326  	}
  2327  
  2328  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2329  	ty := schema.ImpliedType()
  2330  
  2331  	if len(plan.Changes.Resources) != 3 {
  2332  		t.Fatal("expected 3 changes, got", len(plan.Changes.Resources))
  2333  	}
  2334  
  2335  	for _, res := range plan.Changes.Resources {
  2336  		if res.Action != plans.Create {
  2337  			t.Fatalf("expected resource creation, got %s", res.Action)
  2338  		}
  2339  		ric, err := res.Decode(ty)
  2340  		if err != nil {
  2341  			t.Fatal(err)
  2342  		}
  2343  
  2344  		switch i := ric.Addr.String(); i {
  2345  		case "module.child.module.child.aws_instance.foo[0]":
  2346  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2347  				"id":   cty.UnknownVal(cty.String),
  2348  				"type": cty.UnknownVal(cty.String),
  2349  			}), ric.After)
  2350  		case "module.child.module.child.aws_instance.foo[1]":
  2351  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2352  				"id":   cty.UnknownVal(cty.String),
  2353  				"type": cty.UnknownVal(cty.String),
  2354  			}), ric.After)
  2355  		case "module.child.module.child.aws_instance.foo[2]":
  2356  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2357  				"id":   cty.UnknownVal(cty.String),
  2358  				"type": cty.UnknownVal(cty.String),
  2359  			}), ric.After)
  2360  		default:
  2361  			t.Fatal("unknown instance:", i)
  2362  		}
  2363  	}
  2364  }
  2365  
  2366  func TestContext2Plan_countIndex(t *testing.T) {
  2367  	m := testModule(t, "plan-count-index")
  2368  	p := testProvider("aws")
  2369  	p.PlanResourceChangeFn = testDiffFn
  2370  	ctx := testContext2(t, &ContextOpts{
  2371  		Providers: map[addrs.Provider]providers.Factory{
  2372  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2373  		},
  2374  	})
  2375  
  2376  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2377  	if diags.HasErrors() {
  2378  		t.Fatalf("unexpected errors: %s", diags.Err())
  2379  	}
  2380  
  2381  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2382  	ty := schema.ImpliedType()
  2383  
  2384  	if len(plan.Changes.Resources) != 2 {
  2385  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  2386  	}
  2387  
  2388  	for _, res := range plan.Changes.Resources {
  2389  		if res.Action != plans.Create {
  2390  			t.Fatalf("expected resource creation, got %s", res.Action)
  2391  		}
  2392  		ric, err := res.Decode(ty)
  2393  		if err != nil {
  2394  			t.Fatal(err)
  2395  		}
  2396  
  2397  		switch i := ric.Addr.String(); i {
  2398  		case "aws_instance.foo[0]":
  2399  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2400  				"id":   cty.UnknownVal(cty.String),
  2401  				"foo":  cty.StringVal("0"),
  2402  				"type": cty.UnknownVal(cty.String),
  2403  			}), ric.After)
  2404  		case "aws_instance.foo[1]":
  2405  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2406  				"id":   cty.UnknownVal(cty.String),
  2407  				"foo":  cty.StringVal("1"),
  2408  				"type": cty.UnknownVal(cty.String),
  2409  			}), ric.After)
  2410  		default:
  2411  			t.Fatal("unknown instance:", i)
  2412  		}
  2413  	}
  2414  }
  2415  
  2416  func TestContext2Plan_countVar(t *testing.T) {
  2417  	m := testModule(t, "plan-count-var")
  2418  	p := testProvider("aws")
  2419  	p.PlanResourceChangeFn = testDiffFn
  2420  	ctx := testContext2(t, &ContextOpts{
  2421  		Providers: map[addrs.Provider]providers.Factory{
  2422  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2423  		},
  2424  	})
  2425  
  2426  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  2427  		Mode: plans.NormalMode,
  2428  		SetVariables: InputValues{
  2429  			"instance_count": &InputValue{
  2430  				Value:      cty.StringVal("3"),
  2431  				SourceType: ValueFromCaller,
  2432  			},
  2433  		},
  2434  	})
  2435  	if diags.HasErrors() {
  2436  		t.Fatalf("unexpected errors: %s", diags.Err())
  2437  	}
  2438  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2439  	ty := schema.ImpliedType()
  2440  
  2441  	if len(plan.Changes.Resources) != 4 {
  2442  		t.Fatal("expected 4 changes, got", len(plan.Changes.Resources))
  2443  	}
  2444  
  2445  	for _, res := range plan.Changes.Resources {
  2446  		if res.Action != plans.Create {
  2447  			t.Fatalf("expected resource creation, got %s", res.Action)
  2448  		}
  2449  		ric, err := res.Decode(ty)
  2450  		if err != nil {
  2451  			t.Fatal(err)
  2452  		}
  2453  
  2454  		switch i := ric.Addr.String(); i {
  2455  		case "aws_instance.bar":
  2456  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2457  				"id":   cty.UnknownVal(cty.String),
  2458  				"foo":  cty.StringVal("foo,foo,foo"),
  2459  				"type": cty.UnknownVal(cty.String),
  2460  			}), ric.After)
  2461  		case "aws_instance.foo[0]":
  2462  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2463  				"id":   cty.UnknownVal(cty.String),
  2464  				"foo":  cty.StringVal("foo"),
  2465  				"type": cty.UnknownVal(cty.String),
  2466  			}), ric.After)
  2467  		case "aws_instance.foo[1]":
  2468  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2469  				"id":   cty.UnknownVal(cty.String),
  2470  				"foo":  cty.StringVal("foo"),
  2471  				"type": cty.UnknownVal(cty.String),
  2472  			}), ric.After)
  2473  		case "aws_instance.foo[2]":
  2474  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2475  				"id":   cty.UnknownVal(cty.String),
  2476  				"foo":  cty.StringVal("foo"),
  2477  				"type": cty.UnknownVal(cty.String),
  2478  			}), ric.After)
  2479  		default:
  2480  			t.Fatal("unknown instance:", i)
  2481  		}
  2482  	}
  2483  }
  2484  
  2485  func TestContext2Plan_countZero(t *testing.T) {
  2486  	m := testModule(t, "plan-count-zero")
  2487  	p := testProvider("aws")
  2488  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  2489  		ResourceTypes: map[string]*configschema.Block{
  2490  			"aws_instance": {
  2491  				Attributes: map[string]*configschema.Attribute{
  2492  					"foo": {Type: cty.DynamicPseudoType, Optional: true},
  2493  				},
  2494  			},
  2495  		},
  2496  	})
  2497  
  2498  	// This schema contains a DynamicPseudoType, and therefore can't go through any shim functions
  2499  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  2500  		resp.PlannedState = req.ProposedNewState
  2501  		resp.PlannedPrivate = req.PriorPrivate
  2502  		return resp
  2503  	}
  2504  
  2505  	ctx := testContext2(t, &ContextOpts{
  2506  		Providers: map[addrs.Provider]providers.Factory{
  2507  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2508  		},
  2509  	})
  2510  
  2511  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2512  	if diags.HasErrors() {
  2513  		t.Fatalf("unexpected errors: %s", diags.Err())
  2514  	}
  2515  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2516  	ty := schema.ImpliedType()
  2517  
  2518  	if len(plan.Changes.Resources) != 1 {
  2519  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  2520  	}
  2521  
  2522  	res := plan.Changes.Resources[0]
  2523  
  2524  	if res.Action != plans.Create {
  2525  		t.Fatalf("expected resource creation, got %s", res.Action)
  2526  	}
  2527  	ric, err := res.Decode(ty)
  2528  	if err != nil {
  2529  		t.Fatal(err)
  2530  	}
  2531  
  2532  	expected := cty.TupleVal(nil)
  2533  
  2534  	foo := ric.After.GetAttr("foo")
  2535  
  2536  	if !cmp.Equal(expected, foo, valueComparer) {
  2537  		t.Fatal(cmp.Diff(expected, foo, valueComparer))
  2538  	}
  2539  }
  2540  
  2541  func TestContext2Plan_countOneIndex(t *testing.T) {
  2542  	m := testModule(t, "plan-count-one-index")
  2543  	p := testProvider("aws")
  2544  	p.PlanResourceChangeFn = testDiffFn
  2545  	ctx := testContext2(t, &ContextOpts{
  2546  		Providers: map[addrs.Provider]providers.Factory{
  2547  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2548  		},
  2549  	})
  2550  
  2551  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  2552  	if diags.HasErrors() {
  2553  		t.Fatalf("unexpected errors: %s", diags.Err())
  2554  	}
  2555  
  2556  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2557  	ty := schema.ImpliedType()
  2558  
  2559  	if len(plan.Changes.Resources) != 2 {
  2560  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  2561  	}
  2562  
  2563  	for _, res := range plan.Changes.Resources {
  2564  		if res.Action != plans.Create {
  2565  			t.Fatalf("expected resource creation, got %s", res.Action)
  2566  		}
  2567  		ric, err := res.Decode(ty)
  2568  		if err != nil {
  2569  			t.Fatal(err)
  2570  		}
  2571  
  2572  		switch i := ric.Addr.String(); i {
  2573  		case "aws_instance.bar":
  2574  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2575  				"id":   cty.UnknownVal(cty.String),
  2576  				"foo":  cty.StringVal("foo"),
  2577  				"type": cty.UnknownVal(cty.String),
  2578  			}), ric.After)
  2579  		case "aws_instance.foo[0]":
  2580  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2581  				"id":   cty.UnknownVal(cty.String),
  2582  				"foo":  cty.StringVal("foo"),
  2583  				"type": cty.UnknownVal(cty.String),
  2584  			}), ric.After)
  2585  		default:
  2586  			t.Fatal("unknown instance:", i)
  2587  		}
  2588  	}
  2589  }
  2590  
  2591  func TestContext2Plan_countDecreaseToOne(t *testing.T) {
  2592  	m := testModule(t, "plan-count-dec")
  2593  	p := testProvider("aws")
  2594  	p.PlanResourceChangeFn = testDiffFn
  2595  
  2596  	state := states.NewState()
  2597  	root := state.EnsureModule(addrs.RootModuleInstance)
  2598  	root.SetResourceInstanceCurrent(
  2599  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  2600  		&states.ResourceInstanceObjectSrc{
  2601  			Status:    states.ObjectReady,
  2602  			AttrsJSON: []byte(`{"id":"bar","foo":"foo","type":"aws_instance"}`),
  2603  		},
  2604  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2605  	)
  2606  	root.SetResourceInstanceCurrent(
  2607  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  2608  		&states.ResourceInstanceObjectSrc{
  2609  			Status:    states.ObjectReady,
  2610  			AttrsJSON: []byte(`{"id":"bar"}`),
  2611  		},
  2612  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2613  	)
  2614  	root.SetResourceInstanceCurrent(
  2615  		mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
  2616  		&states.ResourceInstanceObjectSrc{
  2617  			Status:    states.ObjectReady,
  2618  			AttrsJSON: []byte(`{"id":"bar"}`),
  2619  		},
  2620  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2621  	)
  2622  
  2623  	ctx := testContext2(t, &ContextOpts{
  2624  		Providers: map[addrs.Provider]providers.Factory{
  2625  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2626  		},
  2627  	})
  2628  
  2629  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  2630  	if diags.HasErrors() {
  2631  		t.Fatalf("unexpected errors: %s", diags.Err())
  2632  	}
  2633  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2634  	ty := schema.ImpliedType()
  2635  
  2636  	if len(plan.Changes.Resources) != 4 {
  2637  		t.Fatal("expected 4 changes, got", len(plan.Changes.Resources))
  2638  	}
  2639  
  2640  	for _, res := range plan.Changes.Resources {
  2641  
  2642  		ric, err := res.Decode(ty)
  2643  		if err != nil {
  2644  			t.Fatal(err)
  2645  		}
  2646  
  2647  		switch i := ric.Addr.String(); i {
  2648  		case "aws_instance.bar":
  2649  			if res.Action != plans.Create {
  2650  				t.Fatalf("expected resource create, got %s", res.Action)
  2651  			}
  2652  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2653  				"id":   cty.UnknownVal(cty.String),
  2654  				"foo":  cty.StringVal("bar"),
  2655  				"type": cty.UnknownVal(cty.String),
  2656  			}), ric.After)
  2657  		case "aws_instance.foo":
  2658  			if res.Action != plans.NoOp {
  2659  				t.Fatalf("resource %s should be unchanged", i)
  2660  			}
  2661  		case "aws_instance.foo[1]":
  2662  			if res.Action != plans.Delete {
  2663  				t.Fatalf("expected resource delete, got %s", res.Action)
  2664  			}
  2665  		case "aws_instance.foo[2]":
  2666  			if res.Action != plans.Delete {
  2667  				t.Fatalf("expected resource delete, got %s", res.Action)
  2668  			}
  2669  		default:
  2670  			t.Fatal("unknown instance:", i)
  2671  		}
  2672  	}
  2673  
  2674  	expectedState := `aws_instance.foo:
  2675    ID = bar
  2676    provider = provider["registry.opentofu.org/hashicorp/aws"]
  2677    foo = foo
  2678    type = aws_instance
  2679  aws_instance.foo.1:
  2680    ID = bar
  2681    provider = provider["registry.opentofu.org/hashicorp/aws"]
  2682  aws_instance.foo.2:
  2683    ID = bar
  2684    provider = provider["registry.opentofu.org/hashicorp/aws"]`
  2685  
  2686  	if plan.PriorState.String() != expectedState {
  2687  		t.Fatalf("epected state:\n%q\n\ngot state:\n%q\n", expectedState, plan.PriorState.String())
  2688  	}
  2689  }
  2690  
  2691  func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) {
  2692  	m := testModule(t, "plan-count-inc")
  2693  	p := testProvider("aws")
  2694  	p.PlanResourceChangeFn = testDiffFn
  2695  
  2696  	state := states.NewState()
  2697  	root := state.EnsureModule(addrs.RootModuleInstance)
  2698  	root.SetResourceInstanceCurrent(
  2699  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  2700  		&states.ResourceInstanceObjectSrc{
  2701  			Status:    states.ObjectReady,
  2702  			AttrsJSON: []byte(`{"id":"bar","type":"aws_instance","foo":"foo"}`),
  2703  		},
  2704  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2705  	)
  2706  
  2707  	ctx := testContext2(t, &ContextOpts{
  2708  		Providers: map[addrs.Provider]providers.Factory{
  2709  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2710  		},
  2711  	})
  2712  
  2713  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  2714  	if diags.HasErrors() {
  2715  		t.Fatalf("unexpected errors: %s", diags.Err())
  2716  	}
  2717  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2718  	ty := schema.ImpliedType()
  2719  
  2720  	if len(plan.Changes.Resources) != 4 {
  2721  		t.Fatal("expected 4 changes, got", len(plan.Changes.Resources))
  2722  	}
  2723  
  2724  	for _, res := range plan.Changes.Resources {
  2725  
  2726  		ric, err := res.Decode(ty)
  2727  		if err != nil {
  2728  			t.Fatal(err)
  2729  		}
  2730  
  2731  		switch i := ric.Addr.String(); i {
  2732  		case "aws_instance.bar":
  2733  			if res.Action != plans.Create {
  2734  				t.Fatalf("expected resource create, got %s", res.Action)
  2735  			}
  2736  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2737  				"id":   cty.UnknownVal(cty.String),
  2738  				"foo":  cty.StringVal("bar"),
  2739  				"type": cty.UnknownVal(cty.String),
  2740  			}), ric.After)
  2741  		case "aws_instance.foo[0]":
  2742  			if res.Action != plans.NoOp {
  2743  				t.Fatalf("resource %s should be unchanged", i)
  2744  			}
  2745  		case "aws_instance.foo[1]":
  2746  			if res.Action != plans.Create {
  2747  				t.Fatalf("expected resource create, got %s", res.Action)
  2748  			}
  2749  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2750  				"id":   cty.UnknownVal(cty.String),
  2751  				"foo":  cty.StringVal("foo"),
  2752  				"type": cty.UnknownVal(cty.String),
  2753  			}), ric.After)
  2754  		case "aws_instance.foo[2]":
  2755  			if res.Action != plans.Create {
  2756  				t.Fatalf("expected resource create, got %s", res.Action)
  2757  			}
  2758  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2759  				"id":   cty.UnknownVal(cty.String),
  2760  				"foo":  cty.StringVal("foo"),
  2761  				"type": cty.UnknownVal(cty.String),
  2762  			}), ric.After)
  2763  		default:
  2764  			t.Fatal("unknown instance:", i)
  2765  		}
  2766  	}
  2767  }
  2768  
  2769  func TestContext2Plan_countIncreaseFromOne(t *testing.T) {
  2770  	m := testModule(t, "plan-count-inc")
  2771  	p := testProvider("aws")
  2772  	p.PlanResourceChangeFn = testDiffFn
  2773  	state := states.NewState()
  2774  	root := state.EnsureModule(addrs.RootModuleInstance)
  2775  	root.SetResourceInstanceCurrent(
  2776  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  2777  		&states.ResourceInstanceObjectSrc{
  2778  			Status:    states.ObjectReady,
  2779  			AttrsJSON: []byte(`{"id":"bar","foo":"foo","type":"aws_instance"}`),
  2780  		},
  2781  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2782  	)
  2783  
  2784  	ctx := testContext2(t, &ContextOpts{
  2785  		Providers: map[addrs.Provider]providers.Factory{
  2786  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2787  		},
  2788  	})
  2789  
  2790  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  2791  	if diags.HasErrors() {
  2792  		t.Fatalf("unexpected errors: %s", diags.Err())
  2793  	}
  2794  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2795  	ty := schema.ImpliedType()
  2796  
  2797  	if len(plan.Changes.Resources) != 4 {
  2798  		t.Fatal("expected 4 changes, got", len(plan.Changes.Resources))
  2799  	}
  2800  
  2801  	for _, res := range plan.Changes.Resources {
  2802  
  2803  		ric, err := res.Decode(ty)
  2804  		if err != nil {
  2805  			t.Fatal(err)
  2806  		}
  2807  
  2808  		switch i := ric.Addr.String(); i {
  2809  		case "aws_instance.bar":
  2810  			if res.Action != plans.Create {
  2811  				t.Fatalf("expected resource create, got %s", res.Action)
  2812  			}
  2813  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2814  				"id":   cty.UnknownVal(cty.String),
  2815  				"foo":  cty.StringVal("bar"),
  2816  				"type": cty.UnknownVal(cty.String),
  2817  			}), ric.After)
  2818  		case "aws_instance.foo[0]":
  2819  			if res.Action != plans.NoOp {
  2820  				t.Fatalf("resource %s should be unchanged", i)
  2821  			}
  2822  		case "aws_instance.foo[1]":
  2823  			if res.Action != plans.Create {
  2824  				t.Fatalf("expected resource create, got %s", res.Action)
  2825  			}
  2826  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2827  				"id":   cty.UnknownVal(cty.String),
  2828  				"foo":  cty.StringVal("foo"),
  2829  				"type": cty.UnknownVal(cty.String),
  2830  			}), ric.After)
  2831  		case "aws_instance.foo[2]":
  2832  			if res.Action != plans.Create {
  2833  				t.Fatalf("expected resource create, got %s", res.Action)
  2834  			}
  2835  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2836  				"id":   cty.UnknownVal(cty.String),
  2837  				"foo":  cty.StringVal("foo"),
  2838  				"type": cty.UnknownVal(cty.String),
  2839  			}), ric.After)
  2840  		default:
  2841  			t.Fatal("unknown instance:", i)
  2842  		}
  2843  	}
  2844  }
  2845  
  2846  // https://github.com/PeoplePerHour/terraform/pull/11
  2847  //
  2848  // This tests a case where both a "resource" and "resource.0" are in
  2849  // the state file, which apparently is a reasonable backwards compatibility
  2850  // concern found in the above 3rd party repo.
  2851  func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) {
  2852  	m := testModule(t, "plan-count-inc")
  2853  	p := testProvider("aws")
  2854  	p.PlanResourceChangeFn = testDiffFn
  2855  
  2856  	state := states.NewState()
  2857  	root := state.EnsureModule(addrs.RootModuleInstance)
  2858  	root.SetResourceInstanceCurrent(
  2859  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  2860  		&states.ResourceInstanceObjectSrc{
  2861  			Status:    states.ObjectReady,
  2862  			AttrsJSON: []byte(`{"id":"bar","foo":"foo","type":"aws_instance"}`),
  2863  		},
  2864  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2865  	)
  2866  	root.SetResourceInstanceCurrent(
  2867  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  2868  		&states.ResourceInstanceObjectSrc{
  2869  			Status:    states.ObjectReady,
  2870  			AttrsJSON: []byte(`{"id":"bar","foo":"foo","type":"aws_instance"}`),
  2871  		},
  2872  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2873  	)
  2874  
  2875  	ctx := testContext2(t, &ContextOpts{
  2876  		Providers: map[addrs.Provider]providers.Factory{
  2877  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  2878  		},
  2879  	})
  2880  
  2881  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  2882  	if diags.HasErrors() {
  2883  		t.Fatalf("unexpected errors: %s", diags.Err())
  2884  	}
  2885  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  2886  	ty := schema.ImpliedType()
  2887  
  2888  	if len(plan.Changes.Resources) != 5 {
  2889  		t.Fatal("expected 5 changes, got", len(plan.Changes.Resources))
  2890  	}
  2891  
  2892  	for _, res := range plan.Changes.Resources {
  2893  
  2894  		ric, err := res.Decode(ty)
  2895  		if err != nil {
  2896  			t.Fatal(err)
  2897  		}
  2898  
  2899  		switch i := ric.Addr.String(); i {
  2900  		case "aws_instance.bar":
  2901  			if res.Action != plans.Create {
  2902  				t.Fatalf("expected resource create, got %s", res.Action)
  2903  			}
  2904  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2905  				"id":   cty.UnknownVal(cty.String),
  2906  				"foo":  cty.StringVal("bar"),
  2907  				"type": cty.UnknownVal(cty.String),
  2908  			}), ric.After)
  2909  		case "aws_instance.foo":
  2910  			if res.Action != plans.Delete {
  2911  				t.Fatalf("resource %s should be removed", i)
  2912  			}
  2913  		case "aws_instance.foo[0]":
  2914  			if res.Action != plans.NoOp {
  2915  				t.Fatalf("resource %s should be unchanged", i)
  2916  			}
  2917  		case "aws_instance.foo[1]":
  2918  			if res.Action != plans.Create {
  2919  				t.Fatalf("expected resource create, got %s", res.Action)
  2920  			}
  2921  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2922  				"id":   cty.UnknownVal(cty.String),
  2923  				"foo":  cty.StringVal("foo"),
  2924  				"type": cty.UnknownVal(cty.String),
  2925  			}), ric.After)
  2926  		case "aws_instance.foo[2]":
  2927  			if res.Action != plans.Create {
  2928  				t.Fatalf("expected resource create, got %s", res.Action)
  2929  			}
  2930  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  2931  				"id":   cty.UnknownVal(cty.String),
  2932  				"foo":  cty.StringVal("foo"),
  2933  				"type": cty.UnknownVal(cty.String),
  2934  			}), ric.After)
  2935  		default:
  2936  			t.Fatal("unknown instance:", i)
  2937  		}
  2938  	}
  2939  }
  2940  
  2941  // A common pattern in TF configs is to have a set of resources with the same
  2942  // count and to use count.index to create correspondences between them:
  2943  //
  2944  //	foo_id = "${foo.bar.*.id[count.index]}"
  2945  //
  2946  // This test is for the situation where some instances already exist and the
  2947  // count is increased. In that case, we should see only the create diffs
  2948  // for the new instances and not any update diffs for the existing ones.
  2949  func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) {
  2950  	m := testModule(t, "plan-count-splat-reference")
  2951  	p := testProvider("aws")
  2952  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  2953  		ResourceTypes: map[string]*configschema.Block{
  2954  			"aws_instance": {
  2955  				Attributes: map[string]*configschema.Attribute{
  2956  					"name":     {Type: cty.String, Optional: true},
  2957  					"foo_name": {Type: cty.String, Optional: true},
  2958  					"id":       {Type: cty.String, Computed: true},
  2959  				},
  2960  			},
  2961  		},
  2962  	})
  2963  
  2964  	state := states.NewState()
  2965  	root := state.EnsureModule(addrs.RootModuleInstance)
  2966  	root.SetResourceInstanceCurrent(
  2967  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  2968  		&states.ResourceInstanceObjectSrc{
  2969  			Status:    states.ObjectReady,
  2970  			AttrsJSON: []byte(`{"id":"bar","name":"foo 0"}`),
  2971  		},
  2972  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2973  	)
  2974  	root.SetResourceInstanceCurrent(
  2975  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  2976  		&states.ResourceInstanceObjectSrc{
  2977  			Status:    states.ObjectReady,
  2978  			AttrsJSON: []byte(`{"id":"bar","name":"foo 1"}`),
  2979  		},
  2980  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2981  	)
  2982  	root.SetResourceInstanceCurrent(
  2983  		mustResourceInstanceAddr("aws_instance.bar[0]").Resource,
  2984  		&states.ResourceInstanceObjectSrc{
  2985  			Status:    states.ObjectReady,
  2986  			AttrsJSON: []byte(`{"id":"bar","foo_name":"foo 0"}`),
  2987  		},
  2988  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2989  	)
  2990  	root.SetResourceInstanceCurrent(
  2991  		mustResourceInstanceAddr("aws_instance.bar[1]").Resource,
  2992  		&states.ResourceInstanceObjectSrc{
  2993  			Status:    states.ObjectReady,
  2994  			AttrsJSON: []byte(`{"id":"bar","foo_name":"foo 1"}`),
  2995  		},
  2996  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  2997  	)
  2998  
  2999  	ctx := testContext2(t, &ContextOpts{
  3000  		Providers: map[addrs.Provider]providers.Factory{
  3001  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3002  		},
  3003  	})
  3004  
  3005  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3006  	if diags.HasErrors() {
  3007  		t.Fatalf("unexpected errors: %s", diags.Err())
  3008  	}
  3009  
  3010  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3011  	ty := schema.ImpliedType()
  3012  
  3013  	if len(plan.Changes.Resources) != 6 {
  3014  		t.Fatal("expected 6 changes, got", len(plan.Changes.Resources))
  3015  	}
  3016  
  3017  	for _, res := range plan.Changes.Resources {
  3018  		ric, err := res.Decode(ty)
  3019  		if err != nil {
  3020  			t.Fatal(err)
  3021  		}
  3022  
  3023  		switch i := ric.Addr.String(); i {
  3024  		case "aws_instance.bar[0]", "aws_instance.bar[1]", "aws_instance.foo[0]", "aws_instance.foo[1]":
  3025  			if res.Action != plans.NoOp {
  3026  				t.Fatalf("resource %s should be unchanged", i)
  3027  			}
  3028  		case "aws_instance.bar[2]":
  3029  			if res.Action != plans.Create {
  3030  				t.Fatalf("expected resource create, got %s", res.Action)
  3031  			}
  3032  			// The instance ID changed, so just check that the name updated
  3033  			if ric.After.GetAttr("foo_name") != cty.StringVal("foo 2") {
  3034  				t.Fatalf("resource %s attr \"foo_name\" should be changed", i)
  3035  			}
  3036  		case "aws_instance.foo[2]":
  3037  			if res.Action != plans.Create {
  3038  				t.Fatalf("expected resource create, got %s", res.Action)
  3039  			}
  3040  			// The instance ID changed, so just check that the name updated
  3041  			if ric.After.GetAttr("name") != cty.StringVal("foo 2") {
  3042  				t.Fatalf("resource %s attr \"name\" should be changed", i)
  3043  			}
  3044  		default:
  3045  			t.Fatal("unknown instance:", i)
  3046  		}
  3047  	}
  3048  }
  3049  
  3050  func TestContext2Plan_forEach(t *testing.T) {
  3051  	m := testModule(t, "plan-for-each")
  3052  	p := testProvider("aws")
  3053  	ctx := testContext2(t, &ContextOpts{
  3054  		Providers: map[addrs.Provider]providers.Factory{
  3055  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3056  		},
  3057  	})
  3058  
  3059  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  3060  	if diags.HasErrors() {
  3061  		t.Fatalf("unexpected errors: %s", diags.Err())
  3062  	}
  3063  
  3064  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3065  	ty := schema.ImpliedType()
  3066  
  3067  	if len(plan.Changes.Resources) != 8 {
  3068  		t.Fatal("expected 8 changes, got", len(plan.Changes.Resources))
  3069  	}
  3070  
  3071  	for _, res := range plan.Changes.Resources {
  3072  		if res.Action != plans.Create {
  3073  			t.Fatalf("expected resource creation, got %s", res.Action)
  3074  		}
  3075  		_, err := res.Decode(ty)
  3076  		if err != nil {
  3077  			t.Fatal(err)
  3078  		}
  3079  	}
  3080  }
  3081  
  3082  func TestContext2Plan_forEachUnknownValue(t *testing.T) {
  3083  	// This module has a variable defined, but it's value is unknown. We
  3084  	// expect this to produce an error, but not to panic.
  3085  	m := testModule(t, "plan-for-each-unknown-value")
  3086  	p := testProvider("aws")
  3087  	ctx := testContext2(t, &ContextOpts{
  3088  		Providers: map[addrs.Provider]providers.Factory{
  3089  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3090  		},
  3091  	})
  3092  
  3093  	_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  3094  		Mode: plans.NormalMode,
  3095  		SetVariables: InputValues{
  3096  			"foo": {
  3097  				Value:      cty.UnknownVal(cty.String),
  3098  				SourceType: ValueFromCLIArg,
  3099  			},
  3100  		},
  3101  	})
  3102  	if !diags.HasErrors() {
  3103  		// Should get this error:
  3104  		// Invalid for_each argument: The "for_each" value depends on resource attributes that cannot be determined until apply...
  3105  		t.Fatal("succeeded; want errors")
  3106  	}
  3107  
  3108  	gotErrStr := diags.Err().Error()
  3109  	wantErrStr := "Invalid for_each argument"
  3110  	if !strings.Contains(gotErrStr, wantErrStr) {
  3111  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  3112  	}
  3113  
  3114  	// We should have a diagnostic that is marked as being caused by unknown
  3115  	// values.
  3116  	for _, diag := range diags {
  3117  		if tfdiags.DiagnosticCausedByUnknown(diag) {
  3118  			return // don't fall through to the error below
  3119  		}
  3120  	}
  3121  	t.Fatalf("no diagnostic is marked as being caused by unknown\n%s", diags.Err().Error())
  3122  }
  3123  
  3124  func TestContext2Plan_destroy(t *testing.T) {
  3125  	m := testModule(t, "plan-destroy")
  3126  	p := testProvider("aws")
  3127  
  3128  	state := states.NewState()
  3129  	root := state.EnsureModule(addrs.RootModuleInstance)
  3130  	root.SetResourceInstanceCurrent(
  3131  		mustResourceInstanceAddr("aws_instance.one").Resource,
  3132  		&states.ResourceInstanceObjectSrc{
  3133  			Status:    states.ObjectReady,
  3134  			AttrsJSON: []byte(`{"id":"bar"}`),
  3135  		},
  3136  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3137  	)
  3138  	root.SetResourceInstanceCurrent(
  3139  		mustResourceInstanceAddr("aws_instance.two").Resource,
  3140  		&states.ResourceInstanceObjectSrc{
  3141  			Status:    states.ObjectReady,
  3142  			AttrsJSON: []byte(`{"id":"baz"}`),
  3143  		},
  3144  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3145  	)
  3146  
  3147  	ctx := testContext2(t, &ContextOpts{
  3148  		Providers: map[addrs.Provider]providers.Factory{
  3149  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3150  		},
  3151  	})
  3152  
  3153  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  3154  		Mode: plans.DestroyMode,
  3155  	})
  3156  	if diags.HasErrors() {
  3157  		t.Fatalf("unexpected errors: %s", diags.Err())
  3158  	}
  3159  
  3160  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3161  	ty := schema.ImpliedType()
  3162  
  3163  	if len(plan.Changes.Resources) != 2 {
  3164  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3165  	}
  3166  
  3167  	for _, res := range plan.Changes.Resources {
  3168  		ric, err := res.Decode(ty)
  3169  		if err != nil {
  3170  			t.Fatal(err)
  3171  		}
  3172  
  3173  		switch i := ric.Addr.String(); i {
  3174  		case "aws_instance.one", "aws_instance.two":
  3175  			if res.Action != plans.Delete {
  3176  				t.Fatalf("resource %s should be removed", i)
  3177  			}
  3178  
  3179  		default:
  3180  			t.Fatal("unknown instance:", i)
  3181  		}
  3182  	}
  3183  }
  3184  
  3185  func TestContext2Plan_moduleDestroy(t *testing.T) {
  3186  	m := testModule(t, "plan-module-destroy")
  3187  	p := testProvider("aws")
  3188  
  3189  	state := states.NewState()
  3190  	root := state.EnsureModule(addrs.RootModuleInstance)
  3191  	root.SetResourceInstanceCurrent(
  3192  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3193  		&states.ResourceInstanceObjectSrc{
  3194  			Status:    states.ObjectReady,
  3195  			AttrsJSON: []byte(`{"id":"bar"}`),
  3196  		},
  3197  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3198  	)
  3199  	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
  3200  	child.SetResourceInstanceCurrent(
  3201  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3202  		&states.ResourceInstanceObjectSrc{
  3203  			Status:    states.ObjectReady,
  3204  			AttrsJSON: []byte(`{"id":"bar"}`),
  3205  		},
  3206  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3207  	)
  3208  
  3209  	ctx := testContext2(t, &ContextOpts{
  3210  		Providers: map[addrs.Provider]providers.Factory{
  3211  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3212  		},
  3213  	})
  3214  
  3215  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  3216  		Mode: plans.DestroyMode,
  3217  	})
  3218  	if diags.HasErrors() {
  3219  		t.Fatalf("unexpected errors: %s", diags.Err())
  3220  	}
  3221  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3222  	ty := schema.ImpliedType()
  3223  
  3224  	if len(plan.Changes.Resources) != 2 {
  3225  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3226  	}
  3227  
  3228  	for _, res := range plan.Changes.Resources {
  3229  		ric, err := res.Decode(ty)
  3230  		if err != nil {
  3231  			t.Fatal(err)
  3232  		}
  3233  
  3234  		switch i := ric.Addr.String(); i {
  3235  		case "aws_instance.foo", "module.child.aws_instance.foo":
  3236  			if res.Action != plans.Delete {
  3237  				t.Fatalf("resource %s should be removed", i)
  3238  			}
  3239  
  3240  		default:
  3241  			t.Fatal("unknown instance:", i)
  3242  		}
  3243  	}
  3244  }
  3245  
  3246  // GH-1835
  3247  func TestContext2Plan_moduleDestroyCycle(t *testing.T) {
  3248  	m := testModule(t, "plan-module-destroy-gh-1835")
  3249  	p := testProvider("aws")
  3250  
  3251  	state := states.NewState()
  3252  	aModule := state.EnsureModule(addrs.RootModuleInstance.Child("a_module", addrs.NoKey))
  3253  	aModule.SetResourceInstanceCurrent(
  3254  		mustResourceInstanceAddr("aws_instance.a").Resource,
  3255  		&states.ResourceInstanceObjectSrc{
  3256  			Status:    states.ObjectReady,
  3257  			AttrsJSON: []byte(`{"id":"a"}`),
  3258  		},
  3259  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3260  	)
  3261  	bModule := state.EnsureModule(addrs.RootModuleInstance.Child("b_module", addrs.NoKey))
  3262  	bModule.SetResourceInstanceCurrent(
  3263  		mustResourceInstanceAddr("aws_instance.b").Resource,
  3264  		&states.ResourceInstanceObjectSrc{
  3265  			Status:    states.ObjectReady,
  3266  			AttrsJSON: []byte(`{"id":"b"}`),
  3267  		},
  3268  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3269  	)
  3270  
  3271  	ctx := testContext2(t, &ContextOpts{
  3272  		Providers: map[addrs.Provider]providers.Factory{
  3273  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3274  		},
  3275  	})
  3276  
  3277  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  3278  		Mode: plans.DestroyMode,
  3279  	})
  3280  	if diags.HasErrors() {
  3281  		t.Fatalf("unexpected errors: %s", diags.Err())
  3282  	}
  3283  
  3284  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3285  	ty := schema.ImpliedType()
  3286  
  3287  	if len(plan.Changes.Resources) != 2 {
  3288  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3289  	}
  3290  
  3291  	for _, res := range plan.Changes.Resources {
  3292  		ric, err := res.Decode(ty)
  3293  		if err != nil {
  3294  			t.Fatal(err)
  3295  		}
  3296  
  3297  		switch i := ric.Addr.String(); i {
  3298  		case "module.a_module.aws_instance.a", "module.b_module.aws_instance.b":
  3299  			if res.Action != plans.Delete {
  3300  				t.Fatalf("resource %s should be removed", i)
  3301  			}
  3302  
  3303  		default:
  3304  			t.Fatal("unknown instance:", i)
  3305  		}
  3306  	}
  3307  }
  3308  
  3309  func TestContext2Plan_moduleDestroyMultivar(t *testing.T) {
  3310  	m := testModule(t, "plan-module-destroy-multivar")
  3311  	p := testProvider("aws")
  3312  
  3313  	state := states.NewState()
  3314  	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
  3315  	child.SetResourceInstanceCurrent(
  3316  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  3317  		&states.ResourceInstanceObjectSrc{
  3318  			Status:    states.ObjectReady,
  3319  			AttrsJSON: []byte(`{"id":"bar0"}`),
  3320  		},
  3321  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3322  	)
  3323  	child.SetResourceInstanceCurrent(
  3324  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  3325  		&states.ResourceInstanceObjectSrc{
  3326  			Status:    states.ObjectReady,
  3327  			AttrsJSON: []byte(`{"id":"bar1"}`),
  3328  		},
  3329  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3330  	)
  3331  
  3332  	ctx := testContext2(t, &ContextOpts{
  3333  		Providers: map[addrs.Provider]providers.Factory{
  3334  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3335  		},
  3336  	})
  3337  
  3338  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  3339  		Mode: plans.DestroyMode,
  3340  	})
  3341  	if diags.HasErrors() {
  3342  		t.Fatalf("unexpected errors: %s", diags.Err())
  3343  	}
  3344  
  3345  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3346  	ty := schema.ImpliedType()
  3347  
  3348  	if len(plan.Changes.Resources) != 2 {
  3349  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3350  	}
  3351  
  3352  	for _, res := range plan.Changes.Resources {
  3353  		ric, err := res.Decode(ty)
  3354  		if err != nil {
  3355  			t.Fatal(err)
  3356  		}
  3357  
  3358  		switch i := ric.Addr.String(); i {
  3359  		case "module.child.aws_instance.foo[0]", "module.child.aws_instance.foo[1]":
  3360  			if res.Action != plans.Delete {
  3361  				t.Fatalf("resource %s should be removed", i)
  3362  			}
  3363  
  3364  		default:
  3365  			t.Fatal("unknown instance:", i)
  3366  		}
  3367  	}
  3368  }
  3369  
  3370  func TestContext2Plan_pathVar(t *testing.T) {
  3371  	cwd, err := os.Getwd()
  3372  	if err != nil {
  3373  		t.Fatalf("err: %s", err)
  3374  	}
  3375  
  3376  	m := testModule(t, "plan-path-var")
  3377  	p := testProvider("aws")
  3378  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  3379  		ResourceTypes: map[string]*configschema.Block{
  3380  			"aws_instance": {
  3381  				Attributes: map[string]*configschema.Attribute{
  3382  					"cwd":    {Type: cty.String, Optional: true},
  3383  					"module": {Type: cty.String, Optional: true},
  3384  					"root":   {Type: cty.String, Optional: true},
  3385  				},
  3386  			},
  3387  		},
  3388  	})
  3389  
  3390  	ctx := testContext2(t, &ContextOpts{
  3391  		Providers: map[addrs.Provider]providers.Factory{
  3392  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3393  		},
  3394  	})
  3395  
  3396  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  3397  	if diags.HasErrors() {
  3398  		t.Fatalf("err: %s", diags.Err())
  3399  	}
  3400  
  3401  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3402  	ty := schema.ImpliedType()
  3403  
  3404  	if len(plan.Changes.Resources) != 1 {
  3405  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  3406  	}
  3407  
  3408  	for _, res := range plan.Changes.Resources {
  3409  		ric, err := res.Decode(ty)
  3410  		if err != nil {
  3411  			t.Fatal(err)
  3412  		}
  3413  
  3414  		switch i := ric.Addr.String(); i {
  3415  		case "aws_instance.foo":
  3416  			if res.Action != plans.Create {
  3417  				t.Fatalf("resource %s should be created", i)
  3418  			}
  3419  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3420  				"cwd":    cty.StringVal(cwd + "/barpath"),
  3421  				"module": cty.StringVal(m.Module.SourceDir + "/foopath"),
  3422  				"root":   cty.StringVal(m.Module.SourceDir + "/barpath"),
  3423  			}), ric.After)
  3424  		default:
  3425  			t.Fatal("unknown instance:", i)
  3426  		}
  3427  	}
  3428  }
  3429  
  3430  func TestContext2Plan_diffVar(t *testing.T) {
  3431  	m := testModule(t, "plan-diffvar")
  3432  	p := testProvider("aws")
  3433  	p.PlanResourceChangeFn = testDiffFn
  3434  	state := states.NewState()
  3435  	root := state.EnsureModule(addrs.RootModuleInstance)
  3436  	root.SetResourceInstanceCurrent(
  3437  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3438  		&states.ResourceInstanceObjectSrc{
  3439  			Status:    states.ObjectReady,
  3440  			AttrsJSON: []byte(`{"id":"bar","num":"2","type":"aws_instance"}`),
  3441  		},
  3442  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3443  	)
  3444  
  3445  	ctx := testContext2(t, &ContextOpts{
  3446  		Providers: map[addrs.Provider]providers.Factory{
  3447  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3448  		},
  3449  	})
  3450  
  3451  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3452  	if diags.HasErrors() {
  3453  		t.Fatalf("unexpected errors: %s", diags.Err())
  3454  	}
  3455  
  3456  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3457  	ty := schema.ImpliedType()
  3458  
  3459  	if len(plan.Changes.Resources) != 2 {
  3460  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3461  	}
  3462  
  3463  	for _, res := range plan.Changes.Resources {
  3464  		ric, err := res.Decode(ty)
  3465  		if err != nil {
  3466  			t.Fatal(err)
  3467  		}
  3468  
  3469  		switch i := ric.Addr.String(); i {
  3470  		case "aws_instance.bar":
  3471  			if res.Action != plans.Create {
  3472  				t.Fatalf("resource %s should be created", i)
  3473  			}
  3474  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3475  				"id":   cty.UnknownVal(cty.String),
  3476  				"num":  cty.NumberIntVal(3),
  3477  				"type": cty.UnknownVal(cty.String),
  3478  			}), ric.After)
  3479  		case "aws_instance.foo":
  3480  			if res.Action != plans.Update {
  3481  				t.Fatalf("resource %s should be updated", i)
  3482  			}
  3483  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3484  				"id":   cty.StringVal("bar"),
  3485  				"num":  cty.NumberIntVal(2),
  3486  				"type": cty.StringVal("aws_instance"),
  3487  			}), ric.Before)
  3488  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3489  				"id":   cty.StringVal("bar"),
  3490  				"num":  cty.NumberIntVal(3),
  3491  				"type": cty.StringVal("aws_instance"),
  3492  			}), ric.After)
  3493  		default:
  3494  			t.Fatal("unknown instance:", i)
  3495  		}
  3496  	}
  3497  }
  3498  
  3499  func TestContext2Plan_hook(t *testing.T) {
  3500  	m := testModule(t, "plan-good")
  3501  	h := new(MockHook)
  3502  	p := testProvider("aws")
  3503  	ctx := testContext2(t, &ContextOpts{
  3504  		Hooks: []Hook{h},
  3505  		Providers: map[addrs.Provider]providers.Factory{
  3506  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3507  		},
  3508  	})
  3509  
  3510  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  3511  	if diags.HasErrors() {
  3512  		t.Fatalf("unexpected errors: %s", diags.Err())
  3513  	}
  3514  
  3515  	if !h.PreDiffCalled {
  3516  		t.Fatal("should be called")
  3517  	}
  3518  	if !h.PostDiffCalled {
  3519  		t.Fatal("should be called")
  3520  	}
  3521  }
  3522  
  3523  func TestContext2Plan_closeProvider(t *testing.T) {
  3524  	// this fixture only has an aliased provider located in the module, to make
  3525  	// sure that the provier name contains a path more complex than
  3526  	// "provider.aws".
  3527  	m := testModule(t, "plan-close-module-provider")
  3528  	p := testProvider("aws")
  3529  	ctx := testContext2(t, &ContextOpts{
  3530  		Providers: map[addrs.Provider]providers.Factory{
  3531  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3532  		},
  3533  	})
  3534  
  3535  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  3536  	if diags.HasErrors() {
  3537  		t.Fatalf("unexpected errors: %s", diags.Err())
  3538  	}
  3539  
  3540  	if !p.CloseCalled {
  3541  		t.Fatal("provider not closed")
  3542  	}
  3543  }
  3544  
  3545  func TestContext2Plan_orphan(t *testing.T) {
  3546  	m := testModule(t, "plan-orphan")
  3547  	p := testProvider("aws")
  3548  	p.PlanResourceChangeFn = testDiffFn
  3549  	state := states.NewState()
  3550  	root := state.EnsureModule(addrs.RootModuleInstance)
  3551  	root.SetResourceInstanceCurrent(
  3552  		mustResourceInstanceAddr("aws_instance.baz").Resource,
  3553  		&states.ResourceInstanceObjectSrc{
  3554  			Status:    states.ObjectReady,
  3555  			AttrsJSON: []byte(`{"id":"bar"}`),
  3556  		},
  3557  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3558  	)
  3559  
  3560  	ctx := testContext2(t, &ContextOpts{
  3561  		Providers: map[addrs.Provider]providers.Factory{
  3562  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3563  		},
  3564  	})
  3565  
  3566  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3567  	if diags.HasErrors() {
  3568  		t.Fatalf("unexpected errors: %s", diags.Err())
  3569  	}
  3570  
  3571  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3572  	ty := schema.ImpliedType()
  3573  
  3574  	if len(plan.Changes.Resources) != 2 {
  3575  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3576  	}
  3577  
  3578  	for _, res := range plan.Changes.Resources {
  3579  		ric, err := res.Decode(ty)
  3580  		if err != nil {
  3581  			t.Fatal(err)
  3582  		}
  3583  
  3584  		switch i := ric.Addr.String(); i {
  3585  		case "aws_instance.baz":
  3586  			if res.Action != plans.Delete {
  3587  				t.Fatalf("resource %s should be removed", i)
  3588  			}
  3589  			if got, want := ric.ActionReason, plans.ResourceInstanceDeleteBecauseNoResourceConfig; got != want {
  3590  				t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3591  			}
  3592  		case "aws_instance.foo":
  3593  			if res.Action != plans.Create {
  3594  				t.Fatalf("resource %s should be created", i)
  3595  			}
  3596  			if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want {
  3597  				t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3598  			}
  3599  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3600  				"id":   cty.UnknownVal(cty.String),
  3601  				"num":  cty.NumberIntVal(2),
  3602  				"type": cty.UnknownVal(cty.String),
  3603  			}), ric.After)
  3604  		default:
  3605  			t.Fatal("unknown instance:", i)
  3606  		}
  3607  	}
  3608  }
  3609  
  3610  // This tests that configurations with UUIDs don't produce errors.
  3611  // For shadows, this would produce errors since a UUID changes every time.
  3612  func TestContext2Plan_shadowUuid(t *testing.T) {
  3613  	m := testModule(t, "plan-shadow-uuid")
  3614  	p := testProvider("aws")
  3615  	ctx := testContext2(t, &ContextOpts{
  3616  		Providers: map[addrs.Provider]providers.Factory{
  3617  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3618  		},
  3619  	})
  3620  
  3621  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  3622  	if diags.HasErrors() {
  3623  		t.Fatalf("unexpected errors: %s", diags.Err())
  3624  	}
  3625  }
  3626  
  3627  func TestContext2Plan_state(t *testing.T) {
  3628  	m := testModule(t, "plan-good")
  3629  	p := testProvider("aws")
  3630  	p.PlanResourceChangeFn = testDiffFn
  3631  	state := states.NewState()
  3632  	root := state.EnsureModule(addrs.RootModuleInstance)
  3633  	root.SetResourceInstanceCurrent(
  3634  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3635  		&states.ResourceInstanceObjectSrc{
  3636  			Status:    states.ObjectReady,
  3637  			AttrsJSON: []byte(`{"id":"bar"}`),
  3638  		},
  3639  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3640  	)
  3641  	ctx := testContext2(t, &ContextOpts{
  3642  		Providers: map[addrs.Provider]providers.Factory{
  3643  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3644  		},
  3645  	})
  3646  
  3647  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3648  	if diags.HasErrors() {
  3649  		t.Fatalf("unexpected errors: %s", diags.Err())
  3650  	}
  3651  
  3652  	if len(plan.Changes.Resources) < 2 {
  3653  		t.Fatalf("bad: %#v", plan.Changes.Resources)
  3654  	}
  3655  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3656  	ty := schema.ImpliedType()
  3657  
  3658  	if len(plan.Changes.Resources) != 2 {
  3659  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3660  	}
  3661  
  3662  	for _, res := range plan.Changes.Resources {
  3663  		ric, err := res.Decode(ty)
  3664  		if err != nil {
  3665  			t.Fatal(err)
  3666  		}
  3667  
  3668  		switch i := ric.Addr.String(); i {
  3669  		case "aws_instance.bar":
  3670  			if res.Action != plans.Create {
  3671  				t.Fatalf("resource %s should be created", i)
  3672  			}
  3673  			if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want {
  3674  				t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3675  			}
  3676  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3677  				"id":   cty.UnknownVal(cty.String),
  3678  				"foo":  cty.StringVal("2"),
  3679  				"type": cty.UnknownVal(cty.String),
  3680  			}), ric.After)
  3681  		case "aws_instance.foo":
  3682  			if res.Action != plans.Update {
  3683  				t.Fatalf("resource %s should be updated", i)
  3684  			}
  3685  			if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want {
  3686  				t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3687  			}
  3688  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3689  				"id":   cty.StringVal("bar"),
  3690  				"num":  cty.NullVal(cty.Number),
  3691  				"type": cty.NullVal(cty.String),
  3692  			}), ric.Before)
  3693  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3694  				"id":   cty.StringVal("bar"),
  3695  				"num":  cty.NumberIntVal(2),
  3696  				"type": cty.UnknownVal(cty.String),
  3697  			}), ric.After)
  3698  		default:
  3699  			t.Fatal("unknown instance:", i)
  3700  		}
  3701  	}
  3702  }
  3703  
  3704  func TestContext2Plan_requiresReplace(t *testing.T) {
  3705  	m := testModule(t, "plan-requires-replace")
  3706  	p := testProvider("test")
  3707  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
  3708  		Provider: providers.Schema{
  3709  			Block: &configschema.Block{},
  3710  		},
  3711  		ResourceTypes: map[string]providers.Schema{
  3712  			"test_thing": {
  3713  				Block: &configschema.Block{
  3714  					Attributes: map[string]*configschema.Attribute{
  3715  						"v": {
  3716  							Type:     cty.String,
  3717  							Required: true,
  3718  						},
  3719  					},
  3720  				},
  3721  			},
  3722  		},
  3723  	}
  3724  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  3725  		return providers.PlanResourceChangeResponse{
  3726  			PlannedState: req.ProposedNewState,
  3727  			RequiresReplace: []cty.Path{
  3728  				cty.GetAttrPath("v"),
  3729  			},
  3730  		}
  3731  	}
  3732  
  3733  	state := states.NewState()
  3734  	root := state.EnsureModule(addrs.RootModuleInstance)
  3735  	root.SetResourceInstanceCurrent(
  3736  		mustResourceInstanceAddr("test_thing.foo").Resource,
  3737  		&states.ResourceInstanceObjectSrc{
  3738  			Status:    states.ObjectReady,
  3739  			AttrsJSON: []byte(`{"v":"hello"}`),
  3740  		},
  3741  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  3742  	)
  3743  
  3744  	ctx := testContext2(t, &ContextOpts{
  3745  		Providers: map[addrs.Provider]providers.Factory{
  3746  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  3747  		},
  3748  	})
  3749  
  3750  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3751  	if diags.HasErrors() {
  3752  		t.Fatalf("unexpected errors: %s", diags.Err())
  3753  	}
  3754  
  3755  	schema := p.GetProviderSchemaResponse.ResourceTypes["test_thing"].Block
  3756  	ty := schema.ImpliedType()
  3757  
  3758  	if got, want := len(plan.Changes.Resources), 1; got != want {
  3759  		t.Fatalf("got %d changes; want %d", got, want)
  3760  	}
  3761  
  3762  	for _, res := range plan.Changes.Resources {
  3763  		t.Run(res.Addr.String(), func(t *testing.T) {
  3764  			ric, err := res.Decode(ty)
  3765  			if err != nil {
  3766  				t.Fatal(err)
  3767  			}
  3768  
  3769  			switch i := ric.Addr.String(); i {
  3770  			case "test_thing.foo":
  3771  				if got, want := ric.Action, plans.DeleteThenCreate; got != want {
  3772  					t.Errorf("wrong action\ngot:  %s\nwant: %s", got, want)
  3773  				}
  3774  				if got, want := ric.ActionReason, plans.ResourceInstanceReplaceBecauseCannotUpdate; got != want {
  3775  					t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3776  				}
  3777  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3778  					"v": cty.StringVal("goodbye"),
  3779  				}), ric.After)
  3780  			default:
  3781  				t.Fatalf("unexpected resource instance %s", i)
  3782  			}
  3783  		})
  3784  	}
  3785  }
  3786  
  3787  func TestContext2Plan_taint(t *testing.T) {
  3788  	m := testModule(t, "plan-taint")
  3789  	p := testProvider("aws")
  3790  	p.PlanResourceChangeFn = testDiffFn
  3791  	state := states.NewState()
  3792  	root := state.EnsureModule(addrs.RootModuleInstance)
  3793  	root.SetResourceInstanceCurrent(
  3794  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3795  		&states.ResourceInstanceObjectSrc{
  3796  			Status:    states.ObjectReady,
  3797  			AttrsJSON: []byte(`{"id":"bar","num":"2","type":"aws_instance"}`),
  3798  		},
  3799  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3800  	)
  3801  	root.SetResourceInstanceCurrent(
  3802  		mustResourceInstanceAddr("aws_instance.bar").Resource,
  3803  		&states.ResourceInstanceObjectSrc{
  3804  			Status:    states.ObjectTainted,
  3805  			AttrsJSON: []byte(`{"id":"baz"}`),
  3806  		},
  3807  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3808  	)
  3809  
  3810  	ctx := testContext2(t, &ContextOpts{
  3811  		Providers: map[addrs.Provider]providers.Factory{
  3812  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3813  		},
  3814  	})
  3815  
  3816  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3817  	if diags.HasErrors() {
  3818  		t.Fatalf("unexpected errors: %s", diags.Err())
  3819  	}
  3820  
  3821  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3822  	ty := schema.ImpliedType()
  3823  
  3824  	if len(plan.Changes.Resources) != 2 {
  3825  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  3826  	}
  3827  
  3828  	for _, res := range plan.Changes.Resources {
  3829  		t.Run(res.Addr.String(), func(t *testing.T) {
  3830  			ric, err := res.Decode(ty)
  3831  			if err != nil {
  3832  				t.Fatal(err)
  3833  			}
  3834  
  3835  			switch i := ric.Addr.String(); i {
  3836  			case "aws_instance.bar":
  3837  				if got, want := res.Action, plans.DeleteThenCreate; got != want {
  3838  					t.Errorf("wrong action\ngot:  %s\nwant: %s", got, want)
  3839  				}
  3840  				if got, want := res.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want {
  3841  					t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3842  				}
  3843  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3844  					"id":   cty.UnknownVal(cty.String),
  3845  					"foo":  cty.StringVal("2"),
  3846  					"type": cty.UnknownVal(cty.String),
  3847  				}), ric.After)
  3848  			case "aws_instance.foo":
  3849  				if got, want := res.Action, plans.NoOp; got != want {
  3850  					t.Errorf("wrong action\ngot:  %s\nwant: %s", got, want)
  3851  				}
  3852  				if got, want := res.ActionReason, plans.ResourceInstanceChangeNoReason; got != want {
  3853  					t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3854  				}
  3855  			default:
  3856  				t.Fatal("unknown instance:", i)
  3857  			}
  3858  		})
  3859  	}
  3860  }
  3861  
  3862  func TestContext2Plan_taintIgnoreChanges(t *testing.T) {
  3863  	m := testModule(t, "plan-taint-ignore-changes")
  3864  	p := testProvider("aws")
  3865  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  3866  		ResourceTypes: map[string]*configschema.Block{
  3867  			"aws_instance": {
  3868  				Attributes: map[string]*configschema.Attribute{
  3869  					"id":   {Type: cty.String, Computed: true},
  3870  					"vars": {Type: cty.String, Optional: true},
  3871  					"type": {Type: cty.String, Computed: true},
  3872  				},
  3873  			},
  3874  		},
  3875  	})
  3876  
  3877  	state := states.NewState()
  3878  	root := state.EnsureModule(addrs.RootModuleInstance)
  3879  	root.SetResourceInstanceCurrent(
  3880  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  3881  		&states.ResourceInstanceObjectSrc{
  3882  			Status:    states.ObjectTainted,
  3883  			AttrsJSON: []byte(`{"id":"foo","vars":"foo","type":"aws_instance"}`),
  3884  		},
  3885  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3886  	)
  3887  
  3888  	ctx := testContext2(t, &ContextOpts{
  3889  		Providers: map[addrs.Provider]providers.Factory{
  3890  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3891  		},
  3892  	})
  3893  
  3894  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  3895  	if diags.HasErrors() {
  3896  		t.Fatalf("unexpected errors: %s", diags.Err())
  3897  	}
  3898  
  3899  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3900  	ty := schema.ImpliedType()
  3901  
  3902  	if len(plan.Changes.Resources) != 1 {
  3903  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  3904  	}
  3905  
  3906  	for _, res := range plan.Changes.Resources {
  3907  		ric, err := res.Decode(ty)
  3908  		if err != nil {
  3909  			t.Fatal(err)
  3910  		}
  3911  
  3912  		switch i := ric.Addr.String(); i {
  3913  		case "aws_instance.foo":
  3914  			if got, want := res.Action, plans.DeleteThenCreate; got != want {
  3915  				t.Errorf("wrong action\ngot:  %s\nwant: %s", got, want)
  3916  			}
  3917  			if got, want := res.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want {
  3918  				t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  3919  			}
  3920  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3921  				"id":   cty.StringVal("foo"),
  3922  				"vars": cty.StringVal("foo"),
  3923  				"type": cty.StringVal("aws_instance"),
  3924  			}), ric.Before)
  3925  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  3926  				"id":   cty.UnknownVal(cty.String),
  3927  				"vars": cty.StringVal("foo"),
  3928  				"type": cty.UnknownVal(cty.String),
  3929  			}), ric.After)
  3930  		default:
  3931  			t.Fatal("unknown instance:", i)
  3932  		}
  3933  	}
  3934  }
  3935  
  3936  // Fails about 50% of the time before the fix for GH-4982, covers the fix.
  3937  func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) {
  3938  	m := testModule(t, "plan-taint-interpolated-count")
  3939  	p := testProvider("aws")
  3940  	p.PlanResourceChangeFn = testDiffFn
  3941  	state := states.NewState()
  3942  	root := state.EnsureModule(addrs.RootModuleInstance)
  3943  	root.SetResourceInstanceCurrent(
  3944  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  3945  		&states.ResourceInstanceObjectSrc{
  3946  			Status:    states.ObjectTainted,
  3947  			AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`),
  3948  		},
  3949  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3950  	)
  3951  	root.SetResourceInstanceCurrent(
  3952  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  3953  		&states.ResourceInstanceObjectSrc{
  3954  			Status:    states.ObjectReady,
  3955  			AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`),
  3956  		},
  3957  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3958  	)
  3959  	root.SetResourceInstanceCurrent(
  3960  		mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
  3961  		&states.ResourceInstanceObjectSrc{
  3962  			Status:    states.ObjectReady,
  3963  			AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`),
  3964  		},
  3965  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  3966  	)
  3967  
  3968  	for i := 0; i < 100; i++ {
  3969  		ctx := testContext2(t, &ContextOpts{
  3970  			Providers: map[addrs.Provider]providers.Factory{
  3971  				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  3972  			},
  3973  		})
  3974  
  3975  		plan, diags := ctx.Plan(m, state.DeepCopy(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  3976  		if diags.HasErrors() {
  3977  			t.Fatalf("unexpected errors: %s", diags.Err())
  3978  		}
  3979  
  3980  		schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  3981  		ty := schema.ImpliedType()
  3982  
  3983  		if len(plan.Changes.Resources) != 3 {
  3984  			t.Fatal("expected 3 changes, got", len(plan.Changes.Resources))
  3985  		}
  3986  
  3987  		for _, res := range plan.Changes.Resources {
  3988  			ric, err := res.Decode(ty)
  3989  			if err != nil {
  3990  				t.Fatal(err)
  3991  			}
  3992  
  3993  			switch i := ric.Addr.String(); i {
  3994  			case "aws_instance.foo[0]":
  3995  				if got, want := ric.Action, plans.DeleteThenCreate; got != want {
  3996  					t.Errorf("wrong action\ngot:  %s\nwant: %s", got, want)
  3997  				}
  3998  				if got, want := ric.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want {
  3999  					t.Errorf("wrong action reason\ngot:  %s\nwant: %s", got, want)
  4000  				}
  4001  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4002  					"id":   cty.StringVal("bar"),
  4003  					"type": cty.StringVal("aws_instance"),
  4004  				}), ric.Before)
  4005  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4006  					"id":   cty.UnknownVal(cty.String),
  4007  					"type": cty.UnknownVal(cty.String),
  4008  				}), ric.After)
  4009  			case "aws_instance.foo[1]", "aws_instance.foo[2]":
  4010  				if res.Action != plans.NoOp {
  4011  					t.Fatalf("resource %s should not be changed", i)
  4012  				}
  4013  			default:
  4014  				t.Fatal("unknown instance:", i)
  4015  			}
  4016  		}
  4017  	}
  4018  }
  4019  
  4020  func TestContext2Plan_targeted(t *testing.T) {
  4021  	m := testModule(t, "plan-targeted")
  4022  	p := testProvider("aws")
  4023  	p.PlanResourceChangeFn = testDiffFn
  4024  	ctx := testContext2(t, &ContextOpts{
  4025  		Providers: map[addrs.Provider]providers.Factory{
  4026  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4027  		},
  4028  	})
  4029  
  4030  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  4031  		Mode: plans.NormalMode,
  4032  		Targets: []addrs.Targetable{
  4033  			addrs.RootModuleInstance.Resource(
  4034  				addrs.ManagedResourceMode, "aws_instance", "foo",
  4035  			),
  4036  		},
  4037  	})
  4038  	if diags.HasErrors() {
  4039  		t.Fatalf("unexpected errors: %s", diags.Err())
  4040  	}
  4041  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4042  	ty := schema.ImpliedType()
  4043  
  4044  	if len(plan.Changes.Resources) != 1 {
  4045  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4046  	}
  4047  
  4048  	for _, res := range plan.Changes.Resources {
  4049  		ric, err := res.Decode(ty)
  4050  		if err != nil {
  4051  			t.Fatal(err)
  4052  		}
  4053  
  4054  		switch i := ric.Addr.String(); i {
  4055  		case "aws_instance.foo":
  4056  			if res.Action != plans.Create {
  4057  				t.Fatalf("resource %s should be created", i)
  4058  			}
  4059  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4060  				"id":   cty.UnknownVal(cty.String),
  4061  				"num":  cty.NumberIntVal(2),
  4062  				"type": cty.UnknownVal(cty.String),
  4063  			}), ric.After)
  4064  		default:
  4065  			t.Fatal("unknown instance:", i)
  4066  		}
  4067  	}
  4068  }
  4069  
  4070  // Test that targeting a module properly plans any inputs that depend
  4071  // on another module.
  4072  func TestContext2Plan_targetedCrossModule(t *testing.T) {
  4073  	m := testModule(t, "plan-targeted-cross-module")
  4074  	p := testProvider("aws")
  4075  	p.PlanResourceChangeFn = testDiffFn
  4076  	ctx := testContext2(t, &ContextOpts{
  4077  		Providers: map[addrs.Provider]providers.Factory{
  4078  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4079  		},
  4080  	})
  4081  
  4082  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  4083  		Mode: plans.NormalMode,
  4084  		Targets: []addrs.Targetable{
  4085  			addrs.RootModuleInstance.Child("B", addrs.NoKey),
  4086  		},
  4087  	})
  4088  	if diags.HasErrors() {
  4089  		t.Fatalf("unexpected errors: %s", diags.Err())
  4090  	}
  4091  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4092  	ty := schema.ImpliedType()
  4093  
  4094  	if len(plan.Changes.Resources) != 2 {
  4095  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  4096  	}
  4097  
  4098  	for _, res := range plan.Changes.Resources {
  4099  		ric, err := res.Decode(ty)
  4100  		if err != nil {
  4101  			t.Fatal(err)
  4102  		}
  4103  		if res.Action != plans.Create {
  4104  			t.Fatalf("resource %s should be created", ric.Addr)
  4105  		}
  4106  		switch i := ric.Addr.String(); i {
  4107  		case "module.A.aws_instance.foo":
  4108  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4109  				"id":   cty.UnknownVal(cty.String),
  4110  				"foo":  cty.StringVal("bar"),
  4111  				"type": cty.UnknownVal(cty.String),
  4112  			}), ric.After)
  4113  		case "module.B.aws_instance.bar":
  4114  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4115  				"id":   cty.UnknownVal(cty.String),
  4116  				"foo":  cty.UnknownVal(cty.String),
  4117  				"type": cty.UnknownVal(cty.String),
  4118  			}), ric.After)
  4119  		default:
  4120  			t.Fatal("unknown instance:", i)
  4121  		}
  4122  	}
  4123  }
  4124  
  4125  func TestContext2Plan_targetedModuleWithProvider(t *testing.T) {
  4126  	m := testModule(t, "plan-targeted-module-with-provider")
  4127  	p := testProvider("null")
  4128  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4129  		Provider: &configschema.Block{
  4130  			Attributes: map[string]*configschema.Attribute{
  4131  				"key": {Type: cty.String, Optional: true},
  4132  			},
  4133  		},
  4134  		ResourceTypes: map[string]*configschema.Block{
  4135  			"null_resource": {
  4136  				Attributes: map[string]*configschema.Attribute{},
  4137  			},
  4138  		},
  4139  	})
  4140  
  4141  	ctx := testContext2(t, &ContextOpts{
  4142  		Providers: map[addrs.Provider]providers.Factory{
  4143  			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
  4144  		},
  4145  	})
  4146  
  4147  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  4148  		Mode: plans.NormalMode,
  4149  		Targets: []addrs.Targetable{
  4150  			addrs.RootModuleInstance.Child("child2", addrs.NoKey),
  4151  		},
  4152  	})
  4153  	if diags.HasErrors() {
  4154  		t.Fatalf("unexpected errors: %s", diags.Err())
  4155  	}
  4156  
  4157  	schema := p.GetProviderSchemaResponse.ResourceTypes["null_resource"].Block
  4158  	ty := schema.ImpliedType()
  4159  
  4160  	if len(plan.Changes.Resources) != 1 {
  4161  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4162  	}
  4163  
  4164  	res := plan.Changes.Resources[0]
  4165  	ric, err := res.Decode(ty)
  4166  	if err != nil {
  4167  		t.Fatal(err)
  4168  	}
  4169  
  4170  	if ric.Addr.String() != "module.child2.null_resource.foo" {
  4171  		t.Fatalf("unexpcetd resource: %s", ric.Addr)
  4172  	}
  4173  }
  4174  
  4175  func TestContext2Plan_targetedOrphan(t *testing.T) {
  4176  	m := testModule(t, "plan-targeted-orphan")
  4177  	p := testProvider("aws")
  4178  
  4179  	state := states.NewState()
  4180  	root := state.EnsureModule(addrs.RootModuleInstance)
  4181  	root.SetResourceInstanceCurrent(
  4182  		mustResourceInstanceAddr("aws_instance.orphan").Resource,
  4183  		&states.ResourceInstanceObjectSrc{
  4184  			Status:    states.ObjectReady,
  4185  			AttrsJSON: []byte(`{"id":"i-789xyz"}`),
  4186  		},
  4187  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4188  	)
  4189  	root.SetResourceInstanceCurrent(
  4190  		mustResourceInstanceAddr("aws_instance.nottargeted").Resource,
  4191  		&states.ResourceInstanceObjectSrc{
  4192  			Status:    states.ObjectReady,
  4193  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  4194  		},
  4195  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4196  	)
  4197  
  4198  	ctx := testContext2(t, &ContextOpts{
  4199  		Providers: map[addrs.Provider]providers.Factory{
  4200  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4201  		},
  4202  	})
  4203  
  4204  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4205  		Mode: plans.DestroyMode,
  4206  		Targets: []addrs.Targetable{
  4207  			addrs.RootModuleInstance.Resource(
  4208  				addrs.ManagedResourceMode, "aws_instance", "orphan",
  4209  			),
  4210  		},
  4211  	})
  4212  	if diags.HasErrors() {
  4213  		t.Fatalf("unexpected errors: %s", diags.Err())
  4214  	}
  4215  
  4216  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4217  	ty := schema.ImpliedType()
  4218  
  4219  	if len(plan.Changes.Resources) != 1 {
  4220  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4221  	}
  4222  
  4223  	for _, res := range plan.Changes.Resources {
  4224  		ric, err := res.Decode(ty)
  4225  		if err != nil {
  4226  			t.Fatal(err)
  4227  		}
  4228  
  4229  		switch i := ric.Addr.String(); i {
  4230  		case "aws_instance.orphan":
  4231  			if res.Action != plans.Delete {
  4232  				t.Fatalf("resource %s should be destroyed", ric.Addr)
  4233  			}
  4234  		default:
  4235  			t.Fatal("unknown instance:", i)
  4236  		}
  4237  	}
  4238  }
  4239  
  4240  // https://github.com/hashicorp/terraform/issues/2538
  4241  func TestContext2Plan_targetedModuleOrphan(t *testing.T) {
  4242  	m := testModule(t, "plan-targeted-module-orphan")
  4243  	p := testProvider("aws")
  4244  
  4245  	state := states.NewState()
  4246  	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
  4247  	child.SetResourceInstanceCurrent(
  4248  		mustResourceInstanceAddr("aws_instance.orphan").Resource,
  4249  		&states.ResourceInstanceObjectSrc{
  4250  			Status:    states.ObjectReady,
  4251  			AttrsJSON: []byte(`{"id":"i-789xyz"}`),
  4252  		},
  4253  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4254  	)
  4255  	child.SetResourceInstanceCurrent(
  4256  		mustResourceInstanceAddr("aws_instance.nottargeted").Resource,
  4257  		&states.ResourceInstanceObjectSrc{
  4258  			Status:    states.ObjectReady,
  4259  			AttrsJSON: []byte(`{"id":"i-abc123"}`),
  4260  		},
  4261  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4262  	)
  4263  
  4264  	ctx := testContext2(t, &ContextOpts{
  4265  		Providers: map[addrs.Provider]providers.Factory{
  4266  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4267  		},
  4268  	})
  4269  
  4270  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4271  		Mode: plans.DestroyMode,
  4272  		Targets: []addrs.Targetable{
  4273  			addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource(
  4274  				addrs.ManagedResourceMode, "aws_instance", "orphan",
  4275  			),
  4276  		},
  4277  	})
  4278  	if diags.HasErrors() {
  4279  		t.Fatalf("unexpected errors: %s", diags.Err())
  4280  	}
  4281  
  4282  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4283  	ty := schema.ImpliedType()
  4284  
  4285  	if len(plan.Changes.Resources) != 1 {
  4286  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4287  	}
  4288  
  4289  	res := plan.Changes.Resources[0]
  4290  	ric, err := res.Decode(ty)
  4291  	if err != nil {
  4292  		t.Fatal(err)
  4293  	}
  4294  
  4295  	if ric.Addr.String() != "module.child.aws_instance.orphan" {
  4296  		t.Fatalf("unexpected resource :%s", ric.Addr)
  4297  	}
  4298  	if res.Action != plans.Delete {
  4299  		t.Fatalf("resource %s should be deleted", ric.Addr)
  4300  	}
  4301  }
  4302  
  4303  func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) {
  4304  	m := testModule(t, "plan-targeted-module-untargeted-variable")
  4305  	p := testProvider("aws")
  4306  	p.PlanResourceChangeFn = testDiffFn
  4307  	ctx := testContext2(t, &ContextOpts{
  4308  		Providers: map[addrs.Provider]providers.Factory{
  4309  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4310  		},
  4311  	})
  4312  
  4313  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  4314  		Targets: []addrs.Targetable{
  4315  			addrs.RootModuleInstance.Resource(
  4316  				addrs.ManagedResourceMode, "aws_instance", "blue",
  4317  			),
  4318  			addrs.RootModuleInstance.Child("blue_mod", addrs.NoKey),
  4319  		},
  4320  	})
  4321  	if diags.HasErrors() {
  4322  		t.Fatalf("unexpected errors: %s", diags.Err())
  4323  	}
  4324  
  4325  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4326  	ty := schema.ImpliedType()
  4327  
  4328  	if len(plan.Changes.Resources) != 2 {
  4329  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  4330  	}
  4331  
  4332  	for _, res := range plan.Changes.Resources {
  4333  		ric, err := res.Decode(ty)
  4334  		if err != nil {
  4335  			t.Fatal(err)
  4336  		}
  4337  		if res.Action != plans.Create {
  4338  			t.Fatalf("resource %s should be created", ric.Addr)
  4339  		}
  4340  		switch i := ric.Addr.String(); i {
  4341  		case "aws_instance.blue":
  4342  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4343  				"id":   cty.UnknownVal(cty.String),
  4344  				"type": cty.UnknownVal(cty.String),
  4345  			}), ric.After)
  4346  		case "module.blue_mod.aws_instance.mod":
  4347  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4348  				"id":    cty.UnknownVal(cty.String),
  4349  				"value": cty.UnknownVal(cty.String),
  4350  				"type":  cty.UnknownVal(cty.String),
  4351  			}), ric.After)
  4352  		default:
  4353  			t.Fatal("unknown instance:", i)
  4354  		}
  4355  	}
  4356  }
  4357  
  4358  // ensure that outputs missing references due to targetting are removed from
  4359  // the graph.
  4360  func TestContext2Plan_outputContainsTargetedResource(t *testing.T) {
  4361  	m := testModule(t, "plan-untargeted-resource-output")
  4362  	p := testProvider("aws")
  4363  	ctx := testContext2(t, &ContextOpts{
  4364  		Providers: map[addrs.Provider]providers.Factory{
  4365  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4366  		},
  4367  	})
  4368  
  4369  	_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  4370  		Targets: []addrs.Targetable{
  4371  			addrs.RootModuleInstance.Child("mod", addrs.NoKey).Resource(
  4372  				addrs.ManagedResourceMode, "aws_instance", "a",
  4373  			),
  4374  		},
  4375  	})
  4376  	if diags.HasErrors() {
  4377  		t.Fatalf("err: %s", diags)
  4378  	}
  4379  	if len(diags) != 1 {
  4380  		t.Fatalf("got %d diagnostics; want 1", diags)
  4381  	}
  4382  	if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
  4383  		t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
  4384  	}
  4385  	if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want {
  4386  		t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
  4387  	}
  4388  }
  4389  
  4390  // https://github.com/hashicorp/terraform/issues/4515
  4391  func TestContext2Plan_targetedOverTen(t *testing.T) {
  4392  	m := testModule(t, "plan-targeted-over-ten")
  4393  	p := testProvider("aws")
  4394  	p.PlanResourceChangeFn = testDiffFn
  4395  
  4396  	state := states.NewState()
  4397  	root := state.EnsureModule(addrs.RootModuleInstance)
  4398  	for i := 0; i < 13; i++ {
  4399  		key := fmt.Sprintf("aws_instance.foo[%d]", i)
  4400  		id := fmt.Sprintf("i-abc%d", i)
  4401  		attrs := fmt.Sprintf(`{"id":"%s","type":"aws_instance"}`, id)
  4402  
  4403  		root.SetResourceInstanceCurrent(
  4404  			mustResourceInstanceAddr(key).Resource,
  4405  			&states.ResourceInstanceObjectSrc{
  4406  				Status:    states.ObjectReady,
  4407  				AttrsJSON: []byte(attrs),
  4408  			},
  4409  			mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4410  		)
  4411  	}
  4412  
  4413  	ctx := testContext2(t, &ContextOpts{
  4414  		Providers: map[addrs.Provider]providers.Factory{
  4415  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4416  		},
  4417  	})
  4418  
  4419  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4420  		Targets: []addrs.Targetable{
  4421  			addrs.RootModuleInstance.ResourceInstance(
  4422  				addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1),
  4423  			),
  4424  		},
  4425  	})
  4426  	if diags.HasErrors() {
  4427  		t.Fatalf("unexpected errors: %s", diags.Err())
  4428  	}
  4429  
  4430  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4431  	ty := schema.ImpliedType()
  4432  
  4433  	for _, res := range plan.Changes.Resources {
  4434  		ric, err := res.Decode(ty)
  4435  		if err != nil {
  4436  			t.Fatal(err)
  4437  		}
  4438  		if res.Action != plans.NoOp {
  4439  			t.Fatalf("unexpected action %s for %s", res.Action, ric.Addr)
  4440  		}
  4441  	}
  4442  }
  4443  
  4444  func TestContext2Plan_provider(t *testing.T) {
  4445  	m := testModule(t, "plan-provider")
  4446  	p := testProvider("aws")
  4447  
  4448  	var value interface{}
  4449  	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
  4450  		value = req.Config.GetAttr("foo").AsString()
  4451  		return
  4452  	}
  4453  
  4454  	ctx := testContext2(t, &ContextOpts{
  4455  		Providers: map[addrs.Provider]providers.Factory{
  4456  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4457  		},
  4458  	})
  4459  	opts := &PlanOpts{
  4460  		Mode: plans.NormalMode,
  4461  		SetVariables: InputValues{
  4462  			"foo": &InputValue{
  4463  				Value:      cty.StringVal("bar"),
  4464  				SourceType: ValueFromCaller,
  4465  			},
  4466  		},
  4467  	}
  4468  
  4469  	if _, err := ctx.Plan(m, states.NewState(), opts); err != nil {
  4470  		t.Fatalf("err: %s", err)
  4471  	}
  4472  
  4473  	if value != "bar" {
  4474  		t.Fatalf("bad: %#v", value)
  4475  	}
  4476  }
  4477  
  4478  func TestContext2Plan_varListErr(t *testing.T) {
  4479  	m := testModule(t, "plan-var-list-err")
  4480  	p := testProvider("aws")
  4481  	ctx := testContext2(t, &ContextOpts{
  4482  		Providers: map[addrs.Provider]providers.Factory{
  4483  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4484  		},
  4485  	})
  4486  
  4487  	_, err := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  4488  
  4489  	if err == nil {
  4490  		t.Fatal("should error")
  4491  	}
  4492  }
  4493  
  4494  func TestContext2Plan_ignoreChanges(t *testing.T) {
  4495  	m := testModule(t, "plan-ignore-changes")
  4496  	p := testProvider("aws")
  4497  	p.PlanResourceChangeFn = testDiffFn
  4498  
  4499  	state := states.NewState()
  4500  	root := state.EnsureModule(addrs.RootModuleInstance)
  4501  	root.SetResourceInstanceCurrent(
  4502  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  4503  		&states.ResourceInstanceObjectSrc{
  4504  			Status:    states.ObjectReady,
  4505  			AttrsJSON: []byte(`{"id":"bar","ami":"ami-abcd1234","type":"aws_instance"}`),
  4506  		},
  4507  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4508  	)
  4509  
  4510  	ctx := testContext2(t, &ContextOpts{
  4511  		Providers: map[addrs.Provider]providers.Factory{
  4512  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4513  		},
  4514  	})
  4515  
  4516  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4517  		Mode: plans.NormalMode,
  4518  		SetVariables: InputValues{
  4519  			"foo": &InputValue{
  4520  				Value:      cty.StringVal("ami-1234abcd"),
  4521  				SourceType: ValueFromCaller,
  4522  			},
  4523  		},
  4524  	})
  4525  	if diags.HasErrors() {
  4526  		t.Fatalf("unexpected errors: %s", diags.Err())
  4527  	}
  4528  
  4529  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4530  	ty := schema.ImpliedType()
  4531  
  4532  	if len(plan.Changes.Resources) != 1 {
  4533  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4534  	}
  4535  
  4536  	res := plan.Changes.Resources[0]
  4537  	ric, err := res.Decode(ty)
  4538  	if err != nil {
  4539  		t.Fatal(err)
  4540  	}
  4541  
  4542  	if ric.Addr.String() != "aws_instance.foo" {
  4543  		t.Fatalf("unexpected resource: %s", ric.Addr)
  4544  	}
  4545  
  4546  	checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4547  		"id":   cty.StringVal("bar"),
  4548  		"ami":  cty.StringVal("ami-abcd1234"),
  4549  		"type": cty.StringVal("aws_instance"),
  4550  	}), ric.After)
  4551  }
  4552  
  4553  func TestContext2Plan_ignoreChangesWildcard(t *testing.T) {
  4554  	m := testModule(t, "plan-ignore-changes-wildcard")
  4555  	p := testProvider("aws")
  4556  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  4557  		// computed attributes should not be set in config
  4558  		id := req.Config.GetAttr("id")
  4559  		if !id.IsNull() {
  4560  			t.Error("computed id set in plan config")
  4561  		}
  4562  
  4563  		foo := req.Config.GetAttr("foo")
  4564  		if foo.IsNull() {
  4565  			t.Error(`missing "foo" during plan, was set to "bar" in state and config`)
  4566  		}
  4567  
  4568  		return testDiffFn(req)
  4569  	}
  4570  
  4571  	state := states.NewState()
  4572  	root := state.EnsureModule(addrs.RootModuleInstance)
  4573  	root.SetResourceInstanceCurrent(
  4574  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  4575  		&states.ResourceInstanceObjectSrc{
  4576  			Status:    states.ObjectReady,
  4577  			AttrsJSON: []byte(`{"id":"bar","ami":"ami-abcd1234","instance":"t2.micro","type":"aws_instance","foo":"bar"}`),
  4578  		},
  4579  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4580  	)
  4581  
  4582  	ctx := testContext2(t, &ContextOpts{
  4583  		Providers: map[addrs.Provider]providers.Factory{
  4584  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4585  		},
  4586  	})
  4587  
  4588  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4589  		Mode: plans.NormalMode,
  4590  		SetVariables: InputValues{
  4591  			"foo": &InputValue{
  4592  				Value:      cty.StringVal("ami-1234abcd"),
  4593  				SourceType: ValueFromCaller,
  4594  			},
  4595  			"bar": &InputValue{
  4596  				Value:      cty.StringVal("t2.small"),
  4597  				SourceType: ValueFromCaller,
  4598  			},
  4599  		},
  4600  	})
  4601  	if diags.HasErrors() {
  4602  		t.Fatalf("unexpected errors: %s", diags.Err())
  4603  	}
  4604  
  4605  	for _, res := range plan.Changes.Resources {
  4606  		if res.Action != plans.NoOp {
  4607  			t.Fatalf("unexpected resource diffs in root module: %s", spew.Sdump(plan.Changes.Resources))
  4608  		}
  4609  	}
  4610  }
  4611  
  4612  func TestContext2Plan_ignoreChangesInMap(t *testing.T) {
  4613  	p := testProvider("test")
  4614  
  4615  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4616  		ResourceTypes: map[string]*configschema.Block{
  4617  			"test_ignore_changes_map": {
  4618  				Attributes: map[string]*configschema.Attribute{
  4619  					"tags": {Type: cty.Map(cty.String), Optional: true},
  4620  				},
  4621  			},
  4622  		},
  4623  	})
  4624  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  4625  		return providers.PlanResourceChangeResponse{
  4626  			PlannedState: req.ProposedNewState,
  4627  		}
  4628  	}
  4629  
  4630  	s := states.BuildState(func(ss *states.SyncState) {
  4631  		ss.SetResourceInstanceCurrent(
  4632  			addrs.Resource{
  4633  				Mode: addrs.ManagedResourceMode,
  4634  				Type: "test_ignore_changes_map",
  4635  				Name: "foo",
  4636  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  4637  			&states.ResourceInstanceObjectSrc{
  4638  				Status:    states.ObjectReady,
  4639  				AttrsJSON: []byte(`{"id":"foo","tags":{"ignored":"from state","other":"from state"},"type":"aws_instance"}`),
  4640  			},
  4641  			addrs.AbsProviderConfig{
  4642  				Provider: addrs.NewDefaultProvider("test"),
  4643  				Module:   addrs.RootModule,
  4644  			},
  4645  		)
  4646  	})
  4647  	m := testModule(t, "plan-ignore-changes-in-map")
  4648  
  4649  	ctx := testContext2(t, &ContextOpts{
  4650  		Providers: map[addrs.Provider]providers.Factory{
  4651  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  4652  		},
  4653  	})
  4654  
  4655  	plan, diags := ctx.Plan(m, s, DefaultPlanOpts)
  4656  	if diags.HasErrors() {
  4657  		t.Fatalf("unexpected errors: %s", diags.Err())
  4658  	}
  4659  
  4660  	schema := p.GetProviderSchemaResponse.ResourceTypes["test_ignore_changes_map"].Block
  4661  	ty := schema.ImpliedType()
  4662  
  4663  	if got, want := len(plan.Changes.Resources), 1; got != want {
  4664  		t.Fatalf("wrong number of changes %d; want %d", got, want)
  4665  	}
  4666  
  4667  	res := plan.Changes.Resources[0]
  4668  	ric, err := res.Decode(ty)
  4669  	if err != nil {
  4670  		t.Fatal(err)
  4671  	}
  4672  	if res.Action != plans.Update {
  4673  		t.Fatalf("resource %s should be updated, got %s", ric.Addr, res.Action)
  4674  	}
  4675  
  4676  	if got, want := ric.Addr.String(), "test_ignore_changes_map.foo"; got != want {
  4677  		t.Fatalf("unexpected resource address %s; want %s", got, want)
  4678  	}
  4679  
  4680  	checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4681  		"tags": cty.MapVal(map[string]cty.Value{
  4682  			"ignored": cty.StringVal("from state"),
  4683  			"other":   cty.StringVal("from config"),
  4684  		}),
  4685  	}), ric.After)
  4686  }
  4687  
  4688  func TestContext2Plan_ignoreChangesSensitive(t *testing.T) {
  4689  	m := testModule(t, "plan-ignore-changes-sensitive")
  4690  	p := testProvider("aws")
  4691  	p.PlanResourceChangeFn = testDiffFn
  4692  
  4693  	state := states.NewState()
  4694  	root := state.EnsureModule(addrs.RootModuleInstance)
  4695  	root.SetResourceInstanceCurrent(
  4696  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  4697  		&states.ResourceInstanceObjectSrc{
  4698  			Status:    states.ObjectReady,
  4699  			AttrsJSON: []byte(`{"id":"bar","ami":"ami-abcd1234","type":"aws_instance"}`),
  4700  		},
  4701  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  4702  	)
  4703  
  4704  	ctx := testContext2(t, &ContextOpts{
  4705  		Providers: map[addrs.Provider]providers.Factory{
  4706  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4707  		},
  4708  	})
  4709  
  4710  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  4711  		Mode: plans.NormalMode,
  4712  		SetVariables: InputValues{
  4713  			"foo": &InputValue{
  4714  				Value:      cty.StringVal("ami-1234abcd"),
  4715  				SourceType: ValueFromCaller,
  4716  			},
  4717  		},
  4718  	})
  4719  	if diags.HasErrors() {
  4720  		t.Fatalf("unexpected errors: %s", diags.Err())
  4721  	}
  4722  
  4723  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  4724  	ty := schema.ImpliedType()
  4725  
  4726  	if len(plan.Changes.Resources) != 1 {
  4727  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  4728  	}
  4729  
  4730  	res := plan.Changes.Resources[0]
  4731  	ric, err := res.Decode(ty)
  4732  	if err != nil {
  4733  		t.Fatal(err)
  4734  	}
  4735  
  4736  	if ric.Addr.String() != "aws_instance.foo" {
  4737  		t.Fatalf("unexpected resource: %s", ric.Addr)
  4738  	}
  4739  
  4740  	checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4741  		"id":   cty.StringVal("bar"),
  4742  		"ami":  cty.StringVal("ami-abcd1234"),
  4743  		"type": cty.StringVal("aws_instance"),
  4744  	}), ric.After)
  4745  }
  4746  
  4747  func TestContext2Plan_moduleMapLiteral(t *testing.T) {
  4748  	m := testModule(t, "plan-module-map-literal")
  4749  	p := testProvider("aws")
  4750  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4751  		ResourceTypes: map[string]*configschema.Block{
  4752  			"aws_instance": {
  4753  				Attributes: map[string]*configschema.Attribute{
  4754  					"meta": {Type: cty.Map(cty.String), Optional: true},
  4755  					"tags": {Type: cty.Map(cty.String), Optional: true},
  4756  				},
  4757  			},
  4758  		},
  4759  	})
  4760  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  4761  		s := req.ProposedNewState.AsValueMap()
  4762  		m := s["tags"].AsValueMap()
  4763  
  4764  		if m["foo"].AsString() != "bar" {
  4765  			t.Fatalf("Bad value in tags attr: %#v", m)
  4766  		}
  4767  
  4768  		meta := s["meta"].AsValueMap()
  4769  		if len(meta) != 0 {
  4770  			t.Fatalf("Meta attr not empty: %#v", meta)
  4771  		}
  4772  		return testDiffFn(req)
  4773  	}
  4774  	ctx := testContext2(t, &ContextOpts{
  4775  		Providers: map[addrs.Provider]providers.Factory{
  4776  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4777  		},
  4778  	})
  4779  
  4780  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  4781  	if diags.HasErrors() {
  4782  		t.Fatalf("unexpected errors: %s", diags.Err())
  4783  	}
  4784  }
  4785  
  4786  func TestContext2Plan_computedValueInMap(t *testing.T) {
  4787  	m := testModule(t, "plan-computed-value-in-map")
  4788  	p := testProvider("aws")
  4789  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4790  		ResourceTypes: map[string]*configschema.Block{
  4791  			"aws_instance": {
  4792  				Attributes: map[string]*configschema.Attribute{
  4793  					"looked_up": {Type: cty.String, Optional: true},
  4794  				},
  4795  			},
  4796  			"aws_computed_source": {
  4797  				Attributes: map[string]*configschema.Attribute{
  4798  					"computed_read_only": {Type: cty.String, Computed: true},
  4799  				},
  4800  			},
  4801  		},
  4802  	})
  4803  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  4804  		resp = testDiffFn(req)
  4805  
  4806  		if req.TypeName != "aws_computed_source" {
  4807  			return
  4808  		}
  4809  
  4810  		planned := resp.PlannedState.AsValueMap()
  4811  		planned["computed_read_only"] = cty.UnknownVal(cty.String)
  4812  		resp.PlannedState = cty.ObjectVal(planned)
  4813  		return resp
  4814  	}
  4815  
  4816  	ctx := testContext2(t, &ContextOpts{
  4817  		Providers: map[addrs.Provider]providers.Factory{
  4818  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4819  		},
  4820  	})
  4821  
  4822  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  4823  	if diags.HasErrors() {
  4824  		t.Fatalf("unexpected errors: %s", diags.Err())
  4825  	}
  4826  
  4827  	if len(plan.Changes.Resources) != 2 {
  4828  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  4829  	}
  4830  
  4831  	for _, res := range plan.Changes.Resources {
  4832  		schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block
  4833  
  4834  		ric, err := res.Decode(schema.ImpliedType())
  4835  		if err != nil {
  4836  			t.Fatal(err)
  4837  		}
  4838  
  4839  		if res.Action != plans.Create {
  4840  			t.Fatalf("resource %s should be created", ric.Addr)
  4841  		}
  4842  
  4843  		switch i := ric.Addr.String(); i {
  4844  		case "aws_computed_source.intermediates":
  4845  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4846  				"computed_read_only": cty.UnknownVal(cty.String),
  4847  			}), ric.After)
  4848  		case "module.test_mod.aws_instance.inner2":
  4849  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4850  				"looked_up": cty.UnknownVal(cty.String),
  4851  			}), ric.After)
  4852  		default:
  4853  			t.Fatal("unknown instance:", i)
  4854  		}
  4855  	}
  4856  }
  4857  
  4858  func TestContext2Plan_moduleVariableFromSplat(t *testing.T) {
  4859  	m := testModule(t, "plan-module-variable-from-splat")
  4860  	p := testProvider("aws")
  4861  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4862  		ResourceTypes: map[string]*configschema.Block{
  4863  			"aws_instance": {
  4864  				Attributes: map[string]*configschema.Attribute{
  4865  					"thing": {Type: cty.String, Optional: true},
  4866  				},
  4867  			},
  4868  		},
  4869  	})
  4870  
  4871  	ctx := testContext2(t, &ContextOpts{
  4872  		Providers: map[addrs.Provider]providers.Factory{
  4873  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4874  		},
  4875  	})
  4876  
  4877  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  4878  	if diags.HasErrors() {
  4879  		t.Fatalf("unexpected errors: %s", diags.Err())
  4880  	}
  4881  
  4882  	if len(plan.Changes.Resources) != 4 {
  4883  		t.Fatal("expected 4 changes, got", len(plan.Changes.Resources))
  4884  	}
  4885  
  4886  	for _, res := range plan.Changes.Resources {
  4887  		schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block
  4888  
  4889  		ric, err := res.Decode(schema.ImpliedType())
  4890  		if err != nil {
  4891  			t.Fatal(err)
  4892  		}
  4893  
  4894  		if res.Action != plans.Create {
  4895  			t.Fatalf("resource %s should be created", ric.Addr)
  4896  		}
  4897  
  4898  		switch i := ric.Addr.String(); i {
  4899  		case "module.mod1.aws_instance.test[0]",
  4900  			"module.mod1.aws_instance.test[1]",
  4901  			"module.mod2.aws_instance.test[0]",
  4902  			"module.mod2.aws_instance.test[1]":
  4903  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4904  				"thing": cty.StringVal("doesnt"),
  4905  			}), ric.After)
  4906  		default:
  4907  			t.Fatal("unknown instance:", i)
  4908  		}
  4909  	}
  4910  }
  4911  
  4912  func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) {
  4913  	m := testModule(t, "plan-cbd-depends-datasource")
  4914  	p := testProvider("aws")
  4915  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  4916  		ResourceTypes: map[string]*configschema.Block{
  4917  			"aws_instance": {
  4918  				Attributes: map[string]*configschema.Attribute{
  4919  					"num":      {Type: cty.String, Optional: true},
  4920  					"computed": {Type: cty.String, Optional: true, Computed: true},
  4921  				},
  4922  			},
  4923  		},
  4924  		DataSources: map[string]*configschema.Block{
  4925  			"aws_vpc": {
  4926  				Attributes: map[string]*configschema.Attribute{
  4927  					"id":  {Type: cty.String, Computed: true},
  4928  					"foo": {Type: cty.Number, Optional: true},
  4929  				},
  4930  			},
  4931  		},
  4932  	})
  4933  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  4934  		computedVal := req.ProposedNewState.GetAttr("computed")
  4935  		if computedVal.IsNull() {
  4936  			computedVal = cty.UnknownVal(cty.String)
  4937  		}
  4938  		return providers.PlanResourceChangeResponse{
  4939  			PlannedState: cty.ObjectVal(map[string]cty.Value{
  4940  				"num":      req.ProposedNewState.GetAttr("num"),
  4941  				"computed": computedVal,
  4942  			}),
  4943  		}
  4944  	}
  4945  	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
  4946  		cfg := req.Config.AsValueMap()
  4947  		cfg["id"] = cty.StringVal("data_id")
  4948  		return providers.ReadDataSourceResponse{
  4949  			State: cty.ObjectVal(cfg),
  4950  		}
  4951  	}
  4952  
  4953  	ctx := testContext2(t, &ContextOpts{
  4954  		Providers: map[addrs.Provider]providers.Factory{
  4955  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  4956  		},
  4957  	})
  4958  
  4959  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  4960  	if diags.HasErrors() {
  4961  		t.Fatalf("unexpected errors: %s", diags.Err())
  4962  	}
  4963  
  4964  	seenAddrs := make(map[string]struct{})
  4965  	for _, res := range plan.Changes.Resources {
  4966  		var schema *configschema.Block
  4967  		switch res.Addr.Resource.Resource.Mode {
  4968  		case addrs.DataResourceMode:
  4969  			schema = p.GetProviderSchemaResponse.DataSources[res.Addr.Resource.Resource.Type].Block
  4970  		case addrs.ManagedResourceMode:
  4971  			schema = p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block
  4972  		}
  4973  
  4974  		ric, err := res.Decode(schema.ImpliedType())
  4975  		if err != nil {
  4976  			t.Fatal(err)
  4977  		}
  4978  
  4979  		seenAddrs[ric.Addr.String()] = struct{}{}
  4980  
  4981  		t.Run(ric.Addr.String(), func(t *testing.T) {
  4982  			switch i := ric.Addr.String(); i {
  4983  			case "aws_instance.foo[0]":
  4984  				if res.Action != plans.Create {
  4985  					t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action)
  4986  				}
  4987  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4988  					"num":      cty.StringVal("2"),
  4989  					"computed": cty.StringVal("data_id"),
  4990  				}), ric.After)
  4991  			case "aws_instance.foo[1]":
  4992  				if res.Action != plans.Create {
  4993  					t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action)
  4994  				}
  4995  				checkVals(t, objectVal(t, schema, map[string]cty.Value{
  4996  					"num":      cty.StringVal("2"),
  4997  					"computed": cty.StringVal("data_id"),
  4998  				}), ric.After)
  4999  			default:
  5000  				t.Fatal("unknown instance:", i)
  5001  			}
  5002  		})
  5003  	}
  5004  
  5005  	wantAddrs := map[string]struct{}{
  5006  		"aws_instance.foo[0]": {},
  5007  		"aws_instance.foo[1]": {},
  5008  	}
  5009  	if !cmp.Equal(seenAddrs, wantAddrs) {
  5010  		t.Errorf("incorrect addresses in changeset:\n%s", cmp.Diff(wantAddrs, seenAddrs))
  5011  	}
  5012  }
  5013  
  5014  // interpolated lists need to be stored in the original order.
  5015  func TestContext2Plan_listOrder(t *testing.T) {
  5016  	m := testModule(t, "plan-list-order")
  5017  	p := testProvider("aws")
  5018  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5019  		ResourceTypes: map[string]*configschema.Block{
  5020  			"aws_instance": {
  5021  				Attributes: map[string]*configschema.Attribute{
  5022  					"foo": {Type: cty.List(cty.String), Optional: true},
  5023  				},
  5024  			},
  5025  		},
  5026  	})
  5027  	ctx := testContext2(t, &ContextOpts{
  5028  		Providers: map[addrs.Provider]providers.Factory{
  5029  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5030  		},
  5031  	})
  5032  
  5033  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5034  	if diags.HasErrors() {
  5035  		t.Fatalf("unexpected errors: %s", diags.Err())
  5036  	}
  5037  
  5038  	changes := plan.Changes
  5039  	rDiffA := changes.ResourceInstance(addrs.Resource{
  5040  		Mode: addrs.ManagedResourceMode,
  5041  		Type: "aws_instance",
  5042  		Name: "a",
  5043  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
  5044  	rDiffB := changes.ResourceInstance(addrs.Resource{
  5045  		Mode: addrs.ManagedResourceMode,
  5046  		Type: "aws_instance",
  5047  		Name: "b",
  5048  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
  5049  
  5050  	if !cmp.Equal(rDiffA.After, rDiffB.After, valueComparer) {
  5051  		t.Fatal(cmp.Diff(rDiffA.After, rDiffB.After, valueComparer))
  5052  	}
  5053  }
  5054  
  5055  // Make sure ignore-changes doesn't interfere with set/list/map diffs.
  5056  // If a resource was being replaced by a RequiresNew attribute that gets
  5057  // ignored, we need to filter the diff properly to properly update rather than
  5058  // replace.
  5059  func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) {
  5060  	m := testModule(t, "plan-ignore-changes-with-flatmaps")
  5061  	p := testProvider("aws")
  5062  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5063  		ResourceTypes: map[string]*configschema.Block{
  5064  			"aws_instance": {
  5065  				Attributes: map[string]*configschema.Attribute{
  5066  					"user_data":   {Type: cty.String, Optional: true},
  5067  					"require_new": {Type: cty.String, Optional: true},
  5068  
  5069  					// This test predates the 0.12 work to integrate cty and
  5070  					// HCL, and so it was ported as-is where its expected
  5071  					// test output was clearly expecting a list of maps here
  5072  					// even though it is named "set".
  5073  					"set": {Type: cty.List(cty.Map(cty.String)), Optional: true},
  5074  					"lst": {Type: cty.List(cty.String), Optional: true},
  5075  				},
  5076  			},
  5077  		},
  5078  	})
  5079  
  5080  	state := states.NewState()
  5081  	root := state.EnsureModule(addrs.RootModuleInstance)
  5082  	root.SetResourceInstanceCurrent(
  5083  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  5084  		&states.ResourceInstanceObjectSrc{
  5085  			Status: states.ObjectReady,
  5086  			AttrsJSON: []byte(`{
  5087  				"user_data":"x","require_new":"",
  5088  				"set":[{"a":"1"}],
  5089  				"lst":["j"]
  5090  			}`),
  5091  		},
  5092  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5093  	)
  5094  
  5095  	ctx := testContext2(t, &ContextOpts{
  5096  		Providers: map[addrs.Provider]providers.Factory{
  5097  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5098  		},
  5099  	})
  5100  
  5101  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  5102  	if diags.HasErrors() {
  5103  		t.Fatalf("unexpected errors: %s", diags.Err())
  5104  	}
  5105  
  5106  	if len(plan.Changes.Resources) != 1 {
  5107  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  5108  	}
  5109  
  5110  	res := plan.Changes.Resources[0]
  5111  	schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block
  5112  
  5113  	ric, err := res.Decode(schema.ImpliedType())
  5114  	if err != nil {
  5115  		t.Fatal(err)
  5116  	}
  5117  
  5118  	if res.Action != plans.Update {
  5119  		t.Fatalf("resource %s should be updated, got %s", ric.Addr, ric.Action)
  5120  	}
  5121  
  5122  	if ric.Addr.String() != "aws_instance.foo" {
  5123  		t.Fatalf("unknown resource: %s", ric.Addr)
  5124  	}
  5125  
  5126  	checkVals(t, objectVal(t, schema, map[string]cty.Value{
  5127  		"lst": cty.ListVal([]cty.Value{
  5128  			cty.StringVal("j"),
  5129  			cty.StringVal("k"),
  5130  		}),
  5131  		"require_new": cty.StringVal(""),
  5132  		"user_data":   cty.StringVal("x"),
  5133  		"set": cty.ListVal([]cty.Value{cty.MapVal(map[string]cty.Value{
  5134  			"a": cty.StringVal("1"),
  5135  			"b": cty.StringVal("2"),
  5136  		})}),
  5137  	}), ric.After)
  5138  }
  5139  
  5140  // TestContext2Plan_resourceNestedCount ensures resource sets that depend on
  5141  // the count of another resource set (ie: count of a data source that depends
  5142  // on another data source's instance count - data.x.foo.*.id) get properly
  5143  // normalized to the indexes they should be. This case comes up when there is
  5144  // an existing state (after an initial apply).
  5145  func TestContext2Plan_resourceNestedCount(t *testing.T) {
  5146  	m := testModule(t, "nested-resource-count-plan")
  5147  	p := testProvider("aws")
  5148  	p.PlanResourceChangeFn = testDiffFn
  5149  	p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
  5150  		return providers.ReadResourceResponse{
  5151  			NewState: req.PriorState,
  5152  		}
  5153  	}
  5154  
  5155  	state := states.NewState()
  5156  	root := state.EnsureModule(addrs.RootModuleInstance)
  5157  	root.SetResourceInstanceCurrent(
  5158  		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
  5159  		&states.ResourceInstanceObjectSrc{
  5160  			Status:    states.ObjectReady,
  5161  			AttrsJSON: []byte(`{"id":"foo0","type":"aws_instance"}`),
  5162  		},
  5163  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5164  	)
  5165  	root.SetResourceInstanceCurrent(
  5166  		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
  5167  		&states.ResourceInstanceObjectSrc{
  5168  			Status:    states.ObjectReady,
  5169  			AttrsJSON: []byte(`{"id":"foo1","type":"aws_instance"}`),
  5170  		},
  5171  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5172  	)
  5173  	root.SetResourceInstanceCurrent(
  5174  		mustResourceInstanceAddr("aws_instance.bar[0]").Resource,
  5175  		&states.ResourceInstanceObjectSrc{
  5176  			Status:       states.ObjectReady,
  5177  			AttrsJSON:    []byte(`{"id":"bar0","type":"aws_instance"}`),
  5178  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")},
  5179  		},
  5180  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5181  	)
  5182  	root.SetResourceInstanceCurrent(
  5183  		mustResourceInstanceAddr("aws_instance.bar[1]").Resource,
  5184  		&states.ResourceInstanceObjectSrc{
  5185  			Status:       states.ObjectReady,
  5186  			AttrsJSON:    []byte(`{"id":"bar1","type":"aws_instance"}`),
  5187  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")},
  5188  		},
  5189  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5190  	)
  5191  	root.SetResourceInstanceCurrent(
  5192  		mustResourceInstanceAddr("aws_instance.baz[0]").Resource,
  5193  		&states.ResourceInstanceObjectSrc{
  5194  			Status:       states.ObjectReady,
  5195  			AttrsJSON:    []byte(`{"id":"baz0","type":"aws_instance"}`),
  5196  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.bar")},
  5197  		},
  5198  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5199  	)
  5200  	root.SetResourceInstanceCurrent(
  5201  		mustResourceInstanceAddr("aws_instance.baz[1]").Resource,
  5202  		&states.ResourceInstanceObjectSrc{
  5203  			Status:       states.ObjectReady,
  5204  			AttrsJSON:    []byte(`{"id":"baz1","type":"aws_instance"}`),
  5205  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.bar")},
  5206  		},
  5207  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5208  	)
  5209  	ctx := testContext2(t, &ContextOpts{
  5210  		Providers: map[addrs.Provider]providers.Factory{
  5211  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5212  		},
  5213  	})
  5214  
  5215  	diags := ctx.Validate(m)
  5216  	if diags.HasErrors() {
  5217  		t.Fatalf("validate errors: %s", diags.Err())
  5218  	}
  5219  
  5220  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  5221  	if diags.HasErrors() {
  5222  		t.Fatalf("plan errors: %s", diags.Err())
  5223  	}
  5224  
  5225  	for _, res := range plan.Changes.Resources {
  5226  		if res.Action != plans.NoOp {
  5227  			t.Fatalf("resource %s should not change, plan returned %s", res.Addr, res.Action)
  5228  		}
  5229  	}
  5230  }
  5231  
  5232  // Higher level test at TestResource_dataSourceListApplyPanic
  5233  func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) {
  5234  	m := testModule(t, "plan-computed-attr-ref-type-mismatch")
  5235  	p := testProvider("aws")
  5236  	p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
  5237  		var diags tfdiags.Diagnostics
  5238  		if req.TypeName == "aws_instance" {
  5239  			amiVal := req.Config.GetAttr("ami")
  5240  			if amiVal.Type() != cty.String {
  5241  				diags = diags.Append(fmt.Errorf("Expected ami to be cty.String, got %#v", amiVal))
  5242  			}
  5243  		}
  5244  		return providers.ValidateResourceConfigResponse{
  5245  			Diagnostics: diags,
  5246  		}
  5247  	}
  5248  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
  5249  		if req.TypeName != "aws_ami_list" {
  5250  			t.Fatalf("Reached apply for unexpected resource type! %s", req.TypeName)
  5251  		}
  5252  		// Pretend like we make a thing and the computed list "ids" is populated
  5253  		s := req.PlannedState.AsValueMap()
  5254  		s["id"] = cty.StringVal("someid")
  5255  		s["ids"] = cty.ListVal([]cty.Value{
  5256  			cty.StringVal("ami-abc123"),
  5257  			cty.StringVal("ami-bcd345"),
  5258  		})
  5259  
  5260  		resp.NewState = cty.ObjectVal(s)
  5261  		return
  5262  	}
  5263  	ctx := testContext2(t, &ContextOpts{
  5264  		Providers: map[addrs.Provider]providers.Factory{
  5265  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5266  		},
  5267  	})
  5268  
  5269  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5270  	if !diags.HasErrors() {
  5271  		t.Fatalf("Succeeded; want type mismatch error for 'ami' argument")
  5272  	}
  5273  
  5274  	expected := `Inappropriate value for attribute "ami"`
  5275  	if errStr := diags.Err().Error(); !strings.Contains(errStr, expected) {
  5276  		t.Fatalf("expected:\n\n%s\n\nto contain:\n\n%s", errStr, expected)
  5277  	}
  5278  }
  5279  
  5280  func TestContext2Plan_selfRef(t *testing.T) {
  5281  	p := testProvider("aws")
  5282  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5283  		ResourceTypes: map[string]*configschema.Block{
  5284  			"aws_instance": {
  5285  				Attributes: map[string]*configschema.Attribute{
  5286  					"foo": {Type: cty.String, Optional: true},
  5287  				},
  5288  			},
  5289  		},
  5290  	})
  5291  
  5292  	m := testModule(t, "plan-self-ref")
  5293  	c := testContext2(t, &ContextOpts{
  5294  		Providers: map[addrs.Provider]providers.Factory{
  5295  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5296  		},
  5297  	})
  5298  
  5299  	diags := c.Validate(m)
  5300  	if diags.HasErrors() {
  5301  		t.Fatalf("unexpected validation failure: %s", diags.Err())
  5302  	}
  5303  
  5304  	_, diags = c.Plan(m, states.NewState(), DefaultPlanOpts)
  5305  	if !diags.HasErrors() {
  5306  		t.Fatalf("plan succeeded; want error")
  5307  	}
  5308  
  5309  	gotErrStr := diags.Err().Error()
  5310  	wantErrStr := "Self-referential block"
  5311  	if !strings.Contains(gotErrStr, wantErrStr) {
  5312  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  5313  	}
  5314  }
  5315  
  5316  func TestContext2Plan_selfRefMulti(t *testing.T) {
  5317  	p := testProvider("aws")
  5318  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5319  		ResourceTypes: map[string]*configschema.Block{
  5320  			"aws_instance": {
  5321  				Attributes: map[string]*configschema.Attribute{
  5322  					"foo": {Type: cty.String, Optional: true},
  5323  				},
  5324  			},
  5325  		},
  5326  	})
  5327  
  5328  	m := testModule(t, "plan-self-ref-multi")
  5329  	c := testContext2(t, &ContextOpts{
  5330  		Providers: map[addrs.Provider]providers.Factory{
  5331  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5332  		},
  5333  	})
  5334  
  5335  	diags := c.Validate(m)
  5336  	if diags.HasErrors() {
  5337  		t.Fatalf("unexpected validation failure: %s", diags.Err())
  5338  	}
  5339  
  5340  	_, diags = c.Plan(m, states.NewState(), DefaultPlanOpts)
  5341  	if !diags.HasErrors() {
  5342  		t.Fatalf("plan succeeded; want error")
  5343  	}
  5344  
  5345  	gotErrStr := diags.Err().Error()
  5346  	wantErrStr := "Self-referential block"
  5347  	if !strings.Contains(gotErrStr, wantErrStr) {
  5348  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  5349  	}
  5350  }
  5351  
  5352  func TestContext2Plan_selfRefMultiAll(t *testing.T) {
  5353  	p := testProvider("aws")
  5354  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5355  		ResourceTypes: map[string]*configschema.Block{
  5356  			"aws_instance": {
  5357  				Attributes: map[string]*configschema.Attribute{
  5358  					"foo": {Type: cty.List(cty.String), Optional: true},
  5359  				},
  5360  			},
  5361  		},
  5362  	})
  5363  
  5364  	m := testModule(t, "plan-self-ref-multi-all")
  5365  	c := testContext2(t, &ContextOpts{
  5366  		Providers: map[addrs.Provider]providers.Factory{
  5367  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5368  		},
  5369  	})
  5370  
  5371  	diags := c.Validate(m)
  5372  	if diags.HasErrors() {
  5373  		t.Fatalf("unexpected validation failure: %s", diags.Err())
  5374  	}
  5375  
  5376  	_, diags = c.Plan(m, states.NewState(), DefaultPlanOpts)
  5377  	if !diags.HasErrors() {
  5378  		t.Fatalf("plan succeeded; want error")
  5379  	}
  5380  
  5381  	gotErrStr := diags.Err().Error()
  5382  
  5383  	// The graph is checked for cycles before we can walk it, so we don't
  5384  	// encounter the self-reference check.
  5385  	// wantErrStr := "Self-referential block"
  5386  	wantErrStr := "Cycle"
  5387  	if !strings.Contains(gotErrStr, wantErrStr) {
  5388  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  5389  	}
  5390  }
  5391  
  5392  func TestContext2Plan_invalidOutput(t *testing.T) {
  5393  	m := testModuleInline(t, map[string]string{
  5394  		"main.tf": `
  5395  data "aws_data_source" "name" {}
  5396  
  5397  output "out" {
  5398    value = data.aws_data_source.name.missing
  5399  }`,
  5400  	})
  5401  
  5402  	p := testProvider("aws")
  5403  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  5404  		State: cty.ObjectVal(map[string]cty.Value{
  5405  			"id":  cty.StringVal("data_id"),
  5406  			"foo": cty.StringVal("foo"),
  5407  		}),
  5408  	}
  5409  
  5410  	ctx := testContext2(t, &ContextOpts{
  5411  		Providers: map[addrs.Provider]providers.Factory{
  5412  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5413  		},
  5414  	})
  5415  
  5416  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5417  	if !diags.HasErrors() {
  5418  		// Should get this error:
  5419  		// Unsupported attribute: This object does not have an attribute named "missing"
  5420  		t.Fatal("succeeded; want errors")
  5421  	}
  5422  
  5423  	gotErrStr := diags.Err().Error()
  5424  	wantErrStr := "Unsupported attribute"
  5425  	if !strings.Contains(gotErrStr, wantErrStr) {
  5426  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  5427  	}
  5428  }
  5429  
  5430  func TestContext2Plan_invalidModuleOutput(t *testing.T) {
  5431  	m := testModuleInline(t, map[string]string{
  5432  		"child/main.tf": `
  5433  data "aws_data_source" "name" {}
  5434  
  5435  output "out" {
  5436    value = "${data.aws_data_source.name.missing}"
  5437  }`,
  5438  		"main.tf": `
  5439  module "child" {
  5440    source = "./child"
  5441  }
  5442  
  5443  resource "aws_instance" "foo" {
  5444    foo = "${module.child.out}"
  5445  }`,
  5446  	})
  5447  
  5448  	p := testProvider("aws")
  5449  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  5450  		State: cty.ObjectVal(map[string]cty.Value{
  5451  			"id":  cty.StringVal("data_id"),
  5452  			"foo": cty.StringVal("foo"),
  5453  		}),
  5454  	}
  5455  
  5456  	ctx := testContext2(t, &ContextOpts{
  5457  		Providers: map[addrs.Provider]providers.Factory{
  5458  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5459  		},
  5460  	})
  5461  
  5462  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5463  	if !diags.HasErrors() {
  5464  		// Should get this error:
  5465  		// Unsupported attribute: This object does not have an attribute named "missing"
  5466  		t.Fatal("succeeded; want errors")
  5467  	}
  5468  
  5469  	gotErrStr := diags.Err().Error()
  5470  	wantErrStr := "Unsupported attribute"
  5471  	if !strings.Contains(gotErrStr, wantErrStr) {
  5472  		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr)
  5473  	}
  5474  }
  5475  
  5476  func TestContext2Plan_variableValidation(t *testing.T) {
  5477  	m := testModuleInline(t, map[string]string{
  5478  		"main.tf": `
  5479  variable "x" {
  5480    default = "bar"
  5481  }
  5482  
  5483  resource "aws_instance" "foo" {
  5484    foo = var.x
  5485  }`,
  5486  	})
  5487  
  5488  	p := testProvider("aws")
  5489  	p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
  5490  		foo := req.Config.GetAttr("foo").AsString()
  5491  		if foo == "bar" {
  5492  			resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo cannot be bar"))
  5493  		}
  5494  		return
  5495  	}
  5496  
  5497  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  5498  		resp.PlannedState = req.ProposedNewState
  5499  		return
  5500  	}
  5501  
  5502  	ctx := testContext2(t, &ContextOpts{
  5503  		Providers: map[addrs.Provider]providers.Factory{
  5504  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5505  		},
  5506  	})
  5507  
  5508  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5509  	if !diags.HasErrors() {
  5510  		// Should get this error:
  5511  		// Unsupported attribute: This object does not have an attribute named "missing"
  5512  		t.Fatal("succeeded; want errors")
  5513  	}
  5514  }
  5515  
  5516  func TestContext2Plan_variableSensitivity(t *testing.T) {
  5517  	m := testModule(t, "plan-variable-sensitivity")
  5518  
  5519  	p := testProvider("aws")
  5520  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  5521  		resp.PlannedState = req.ProposedNewState
  5522  		return
  5523  	}
  5524  
  5525  	ctx := testContext2(t, &ContextOpts{
  5526  		Providers: map[addrs.Provider]providers.Factory{
  5527  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5528  		},
  5529  	})
  5530  
  5531  	plan, diags := ctx.Plan(m, states.NewState(), SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  5532  	if diags.HasErrors() {
  5533  		t.Fatalf("unexpected errors: %s", diags.Err())
  5534  	}
  5535  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  5536  	ty := schema.ImpliedType()
  5537  
  5538  	if len(plan.Changes.Resources) != 1 {
  5539  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  5540  	}
  5541  
  5542  	for _, res := range plan.Changes.Resources {
  5543  		if res.Action != plans.Create {
  5544  			t.Fatalf("expected resource creation, got %s", res.Action)
  5545  		}
  5546  		ric, err := res.Decode(ty)
  5547  		if err != nil {
  5548  			t.Fatal(err)
  5549  		}
  5550  
  5551  		switch i := ric.Addr.String(); i {
  5552  		case "aws_instance.foo":
  5553  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  5554  				"foo": cty.StringVal("foo").Mark(marks.Sensitive),
  5555  			}), ric.After)
  5556  			if len(res.ChangeSrc.BeforeValMarks) != 0 {
  5557  				t.Errorf("unexpected BeforeValMarks: %#v", res.ChangeSrc.BeforeValMarks)
  5558  			}
  5559  			if len(res.ChangeSrc.AfterValMarks) != 1 {
  5560  				t.Errorf("unexpected AfterValMarks: %#v", res.ChangeSrc.AfterValMarks)
  5561  				continue
  5562  			}
  5563  			pvm := res.ChangeSrc.AfterValMarks[0]
  5564  			if got, want := pvm.Path, cty.GetAttrPath("foo"); !got.Equals(want) {
  5565  				t.Errorf("unexpected path for mark\n got: %#v\nwant: %#v", got, want)
  5566  			}
  5567  			if got, want := pvm.Marks, cty.NewValueMarks(marks.Sensitive); !got.Equal(want) {
  5568  				t.Errorf("unexpected value for mark\n got: %#v\nwant: %#v", got, want)
  5569  			}
  5570  		default:
  5571  			t.Fatal("unknown instance:", i)
  5572  		}
  5573  	}
  5574  }
  5575  
  5576  func TestContext2Plan_variableSensitivityModule(t *testing.T) {
  5577  	m := testModule(t, "plan-variable-sensitivity-module")
  5578  
  5579  	p := testProvider("aws")
  5580  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  5581  		resp.PlannedState = req.ProposedNewState
  5582  		return
  5583  	}
  5584  
  5585  	ctx := testContext2(t, &ContextOpts{
  5586  		Providers: map[addrs.Provider]providers.Factory{
  5587  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5588  		},
  5589  	})
  5590  
  5591  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  5592  		Mode: plans.NormalMode,
  5593  		SetVariables: InputValues{
  5594  			"sensitive_var": {Value: cty.NilVal},
  5595  			"another_var": &InputValue{
  5596  				Value:      cty.StringVal("boop"),
  5597  				SourceType: ValueFromCaller,
  5598  			},
  5599  		},
  5600  	})
  5601  	if diags.HasErrors() {
  5602  		t.Fatalf("unexpected errors: %s", diags.Err())
  5603  	}
  5604  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  5605  	ty := schema.ImpliedType()
  5606  
  5607  	if len(plan.Changes.Resources) != 1 {
  5608  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  5609  	}
  5610  
  5611  	for _, res := range plan.Changes.Resources {
  5612  		if res.Action != plans.Create {
  5613  			t.Fatalf("expected resource creation, got %s", res.Action)
  5614  		}
  5615  		ric, err := res.Decode(ty)
  5616  		if err != nil {
  5617  			t.Fatal(err)
  5618  		}
  5619  
  5620  		switch i := ric.Addr.String(); i {
  5621  		case "module.child.aws_instance.foo":
  5622  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  5623  				"foo":   cty.StringVal("foo").Mark(marks.Sensitive),
  5624  				"value": cty.StringVal("boop").Mark(marks.Sensitive),
  5625  			}), ric.After)
  5626  			if len(res.ChangeSrc.BeforeValMarks) != 0 {
  5627  				t.Errorf("unexpected BeforeValMarks: %#v", res.ChangeSrc.BeforeValMarks)
  5628  			}
  5629  			if len(res.ChangeSrc.AfterValMarks) != 2 {
  5630  				t.Errorf("expected AfterValMarks to contain two elements: %#v", res.ChangeSrc.AfterValMarks)
  5631  				continue
  5632  			}
  5633  			// validate that the after marks have "foo" and "value"
  5634  			contains := func(pvmSlice []cty.PathValueMarks, stepName string) bool {
  5635  				for _, pvm := range pvmSlice {
  5636  					if pvm.Path.Equals(cty.GetAttrPath(stepName)) {
  5637  						if pvm.Marks.Equal(cty.NewValueMarks(marks.Sensitive)) {
  5638  							return true
  5639  						}
  5640  					}
  5641  				}
  5642  				return false
  5643  			}
  5644  			if !contains(res.ChangeSrc.AfterValMarks, "foo") {
  5645  				t.Error("unexpected AfterValMarks to contain \"foo\" with sensitive mark")
  5646  			}
  5647  			if !contains(res.ChangeSrc.AfterValMarks, "value") {
  5648  				t.Error("unexpected AfterValMarks to contain \"value\" with sensitive mark")
  5649  			}
  5650  		default:
  5651  			t.Fatal("unknown instance:", i)
  5652  		}
  5653  	}
  5654  }
  5655  
  5656  func checkVals(t *testing.T, expected, got cty.Value) {
  5657  	t.Helper()
  5658  	// The GoStringer format seems to result in the closest thing to a useful
  5659  	// diff for values with marks.
  5660  	// TODO: if we want to continue using cmp.Diff on cty.Values, we should
  5661  	// make a transformer that creates a more comparable structure.
  5662  	valueTrans := cmp.Transformer("gostring", func(v cty.Value) string {
  5663  		return fmt.Sprintf("%#v\n", v)
  5664  	})
  5665  	if !cmp.Equal(expected, got, valueComparer, typeComparer, equateEmpty) {
  5666  		t.Fatal(cmp.Diff(expected, got, valueTrans, equateEmpty))
  5667  	}
  5668  }
  5669  
  5670  func objectVal(t *testing.T, schema *configschema.Block, m map[string]cty.Value) cty.Value {
  5671  	t.Helper()
  5672  	v, err := schema.CoerceValue(
  5673  		cty.ObjectVal(m),
  5674  	)
  5675  	if err != nil {
  5676  		t.Fatal(err)
  5677  	}
  5678  	return v
  5679  }
  5680  
  5681  func TestContext2Plan_requiredModuleOutput(t *testing.T) {
  5682  	m := testModule(t, "plan-required-output")
  5683  	p := testProvider("test")
  5684  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5685  		ResourceTypes: map[string]*configschema.Block{
  5686  			"test_resource": {
  5687  				Attributes: map[string]*configschema.Attribute{
  5688  					"id":       {Type: cty.String, Computed: true},
  5689  					"required": {Type: cty.String, Required: true},
  5690  				},
  5691  			},
  5692  		},
  5693  	})
  5694  
  5695  	ctx := testContext2(t, &ContextOpts{
  5696  		Providers: map[addrs.Provider]providers.Factory{
  5697  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  5698  		},
  5699  	})
  5700  
  5701  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5702  	if diags.HasErrors() {
  5703  		t.Fatalf("unexpected errors: %s", diags.Err())
  5704  	}
  5705  
  5706  	schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block
  5707  	ty := schema.ImpliedType()
  5708  
  5709  	if len(plan.Changes.Resources) != 2 {
  5710  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  5711  	}
  5712  
  5713  	for _, res := range plan.Changes.Resources {
  5714  		t.Run(fmt.Sprintf("%s %s", res.Action, res.Addr), func(t *testing.T) {
  5715  			if res.Action != plans.Create {
  5716  				t.Fatalf("expected resource creation, got %s", res.Action)
  5717  			}
  5718  			ric, err := res.Decode(ty)
  5719  			if err != nil {
  5720  				t.Fatal(err)
  5721  			}
  5722  
  5723  			var expected cty.Value
  5724  			switch i := ric.Addr.String(); i {
  5725  			case "test_resource.root":
  5726  				expected = objectVal(t, schema, map[string]cty.Value{
  5727  					"id":       cty.UnknownVal(cty.String),
  5728  					"required": cty.UnknownVal(cty.String),
  5729  				})
  5730  			case "module.mod.test_resource.for_output":
  5731  				expected = objectVal(t, schema, map[string]cty.Value{
  5732  					"id":       cty.UnknownVal(cty.String),
  5733  					"required": cty.StringVal("val"),
  5734  				})
  5735  			default:
  5736  				t.Fatal("unknown instance:", i)
  5737  			}
  5738  
  5739  			checkVals(t, expected, ric.After)
  5740  		})
  5741  	}
  5742  }
  5743  
  5744  func TestContext2Plan_requiredModuleObject(t *testing.T) {
  5745  	m := testModule(t, "plan-required-whole-mod")
  5746  	p := testProvider("test")
  5747  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  5748  		ResourceTypes: map[string]*configschema.Block{
  5749  			"test_resource": {
  5750  				Attributes: map[string]*configschema.Attribute{
  5751  					"id":       {Type: cty.String, Computed: true},
  5752  					"required": {Type: cty.String, Required: true},
  5753  				},
  5754  			},
  5755  		},
  5756  	})
  5757  
  5758  	ctx := testContext2(t, &ContextOpts{
  5759  		Providers: map[addrs.Provider]providers.Factory{
  5760  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  5761  		},
  5762  	})
  5763  
  5764  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5765  	if diags.HasErrors() {
  5766  		t.Fatalf("unexpected errors: %s", diags.Err())
  5767  	}
  5768  
  5769  	schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block
  5770  	ty := schema.ImpliedType()
  5771  
  5772  	if len(plan.Changes.Resources) != 2 {
  5773  		t.Fatal("expected 2 changes, got", len(plan.Changes.Resources))
  5774  	}
  5775  
  5776  	for _, res := range plan.Changes.Resources {
  5777  		t.Run(fmt.Sprintf("%s %s", res.Action, res.Addr), func(t *testing.T) {
  5778  			if res.Action != plans.Create {
  5779  				t.Fatalf("expected resource creation, got %s", res.Action)
  5780  			}
  5781  			ric, err := res.Decode(ty)
  5782  			if err != nil {
  5783  				t.Fatal(err)
  5784  			}
  5785  
  5786  			var expected cty.Value
  5787  			switch i := ric.Addr.String(); i {
  5788  			case "test_resource.root":
  5789  				expected = objectVal(t, schema, map[string]cty.Value{
  5790  					"id":       cty.UnknownVal(cty.String),
  5791  					"required": cty.UnknownVal(cty.String),
  5792  				})
  5793  			case "module.mod.test_resource.for_output":
  5794  				expected = objectVal(t, schema, map[string]cty.Value{
  5795  					"id":       cty.UnknownVal(cty.String),
  5796  					"required": cty.StringVal("val"),
  5797  				})
  5798  			default:
  5799  				t.Fatal("unknown instance:", i)
  5800  			}
  5801  
  5802  			checkVals(t, expected, ric.After)
  5803  		})
  5804  	}
  5805  }
  5806  
  5807  func TestContext2Plan_expandOrphan(t *testing.T) {
  5808  	m := testModuleInline(t, map[string]string{
  5809  		"main.tf": `
  5810  module "mod" {
  5811    count = 1
  5812    source = "./mod"
  5813  }
  5814  `,
  5815  		"mod/main.tf": `
  5816  resource "aws_instance" "foo" {
  5817  }
  5818  `,
  5819  	})
  5820  
  5821  	state := states.NewState()
  5822  	state.EnsureModule(addrs.RootModuleInstance.Child("mod", addrs.IntKey(0))).SetResourceInstanceCurrent(
  5823  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  5824  		&states.ResourceInstanceObjectSrc{
  5825  			Status:    states.ObjectReady,
  5826  			AttrsJSON: []byte(`{"id":"child","type":"aws_instance"}`),
  5827  		},
  5828  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5829  	)
  5830  	state.EnsureModule(addrs.RootModuleInstance.Child("mod", addrs.IntKey(1))).SetResourceInstanceCurrent(
  5831  		mustResourceInstanceAddr("aws_instance.foo").Resource,
  5832  		&states.ResourceInstanceObjectSrc{
  5833  			Status:    states.ObjectReady,
  5834  			AttrsJSON: []byte(`{"id":"child","type":"aws_instance"}`),
  5835  		},
  5836  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/aws"]`),
  5837  	)
  5838  
  5839  	p := testProvider("aws")
  5840  	p.PlanResourceChangeFn = testDiffFn
  5841  	ctx := testContext2(t, &ContextOpts{
  5842  		Providers: map[addrs.Provider]providers.Factory{
  5843  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5844  		},
  5845  	})
  5846  
  5847  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  5848  	if diags.HasErrors() {
  5849  		t.Fatal(diags.ErrWithWarnings())
  5850  	}
  5851  
  5852  	expected := map[string]plans.Action{
  5853  		`module.mod[1].aws_instance.foo`: plans.Delete,
  5854  		`module.mod[0].aws_instance.foo`: plans.NoOp,
  5855  	}
  5856  
  5857  	for _, res := range plan.Changes.Resources {
  5858  		want := expected[res.Addr.String()]
  5859  		if res.Action != want {
  5860  			t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action)
  5861  		}
  5862  		delete(expected, res.Addr.String())
  5863  	}
  5864  
  5865  	for res, action := range expected {
  5866  		t.Errorf("missing %s change for %s", action, res)
  5867  	}
  5868  }
  5869  
  5870  func TestContext2Plan_indexInVar(t *testing.T) {
  5871  	m := testModuleInline(t, map[string]string{
  5872  		"main.tf": `
  5873  module "a" {
  5874    count = 1
  5875    source = "./mod"
  5876    in = "test"
  5877  }
  5878  
  5879  module "b" {
  5880    count = 1
  5881    source = "./mod"
  5882    in = length(module.a)
  5883  }
  5884  `,
  5885  		"mod/main.tf": `
  5886  resource "aws_instance" "foo" {
  5887    foo = var.in
  5888  }
  5889  
  5890  variable "in" {
  5891  }
  5892  
  5893  output"out" {
  5894    value = aws_instance.foo.id
  5895  }
  5896  `,
  5897  	})
  5898  
  5899  	p := testProvider("aws")
  5900  	ctx := testContext2(t, &ContextOpts{
  5901  		Providers: map[addrs.Provider]providers.Factory{
  5902  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5903  		},
  5904  	})
  5905  
  5906  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  5907  	if diags.HasErrors() {
  5908  		t.Fatal(diags.ErrWithWarnings())
  5909  	}
  5910  }
  5911  
  5912  func TestContext2Plan_targetExpandedAddress(t *testing.T) {
  5913  	m := testModuleInline(t, map[string]string{
  5914  		"main.tf": `
  5915  module "mod" {
  5916    count = 3
  5917    source = "./mod"
  5918  }
  5919  `,
  5920  		"mod/main.tf": `
  5921  resource "aws_instance" "foo" {
  5922    count = 2
  5923  }
  5924  `,
  5925  	})
  5926  
  5927  	p := testProvider("aws")
  5928  
  5929  	targets := []addrs.Targetable{}
  5930  	target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo[0]")
  5931  	if diags.HasErrors() {
  5932  		t.Fatal(diags.ErrWithWarnings())
  5933  	}
  5934  	targets = append(targets, target.Subject)
  5935  
  5936  	target, diags = addrs.ParseTargetStr("module.mod[2]")
  5937  	if diags.HasErrors() {
  5938  		t.Fatal(diags.ErrWithWarnings())
  5939  	}
  5940  	targets = append(targets, target.Subject)
  5941  
  5942  	ctx := testContext2(t, &ContextOpts{
  5943  		Providers: map[addrs.Provider]providers.Factory{
  5944  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  5945  		},
  5946  	})
  5947  
  5948  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  5949  		Mode:    plans.NormalMode,
  5950  		Targets: targets,
  5951  	})
  5952  	if diags.HasErrors() {
  5953  		t.Fatal(diags.ErrWithWarnings())
  5954  	}
  5955  
  5956  	expected := map[string]plans.Action{
  5957  		// the single targeted mod[1] instances
  5958  		`module.mod[1].aws_instance.foo[0]`: plans.Create,
  5959  		// the whole mode[2]
  5960  		`module.mod[2].aws_instance.foo[0]`: plans.Create,
  5961  		`module.mod[2].aws_instance.foo[1]`: plans.Create,
  5962  	}
  5963  
  5964  	for _, res := range plan.Changes.Resources {
  5965  		want := expected[res.Addr.String()]
  5966  		if res.Action != want {
  5967  			t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action)
  5968  		}
  5969  		delete(expected, res.Addr.String())
  5970  	}
  5971  
  5972  	for res, action := range expected {
  5973  		t.Errorf("missing %s change for %s", action, res)
  5974  	}
  5975  }
  5976  
  5977  func TestContext2Plan_targetResourceInModuleInstance(t *testing.T) {
  5978  	m := testModuleInline(t, map[string]string{
  5979  		"main.tf": `
  5980  module "mod" {
  5981    count = 3
  5982    source = "./mod"
  5983  }
  5984  `,
  5985  		"mod/main.tf": `
  5986  resource "aws_instance" "foo" {
  5987  }
  5988  `,
  5989  	})
  5990  
  5991  	p := testProvider("aws")
  5992  
  5993  	target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo")
  5994  	if diags.HasErrors() {
  5995  		t.Fatal(diags.ErrWithWarnings())
  5996  	}
  5997  
  5998  	targets := []addrs.Targetable{target.Subject}
  5999  
  6000  	ctx := testContext2(t, &ContextOpts{
  6001  		Providers: map[addrs.Provider]providers.Factory{
  6002  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  6003  		},
  6004  	})
  6005  
  6006  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  6007  		Mode:    plans.NormalMode,
  6008  		Targets: targets,
  6009  	})
  6010  	if diags.HasErrors() {
  6011  		t.Fatal(diags.ErrWithWarnings())
  6012  	}
  6013  
  6014  	expected := map[string]plans.Action{
  6015  		// the single targeted mod[1] instance
  6016  		`module.mod[1].aws_instance.foo`: plans.Create,
  6017  	}
  6018  
  6019  	for _, res := range plan.Changes.Resources {
  6020  		want := expected[res.Addr.String()]
  6021  		if res.Action != want {
  6022  			t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action)
  6023  		}
  6024  		delete(expected, res.Addr.String())
  6025  	}
  6026  
  6027  	for res, action := range expected {
  6028  		t.Errorf("missing %s change for %s", action, res)
  6029  	}
  6030  }
  6031  
  6032  func TestContext2Plan_moduleRefIndex(t *testing.T) {
  6033  	m := testModuleInline(t, map[string]string{
  6034  		"main.tf": `
  6035  module "mod" {
  6036    for_each = {
  6037      a = "thing"
  6038    }
  6039    in = null
  6040    source = "./mod"
  6041  }
  6042  
  6043  module "single" {
  6044    source = "./mod"
  6045    in = module.mod["a"]
  6046  }
  6047  `,
  6048  		"mod/main.tf": `
  6049  variable "in" {
  6050  }
  6051  
  6052  output "out" {
  6053    value = "foo"
  6054  }
  6055  
  6056  resource "aws_instance" "foo" {
  6057  }
  6058  `,
  6059  	})
  6060  
  6061  	p := testProvider("aws")
  6062  
  6063  	ctx := testContext2(t, &ContextOpts{
  6064  		Providers: map[addrs.Provider]providers.Factory{
  6065  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  6066  		},
  6067  	})
  6068  
  6069  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6070  	if diags.HasErrors() {
  6071  		t.Fatal(diags.ErrWithWarnings())
  6072  	}
  6073  }
  6074  
  6075  func TestContext2Plan_noChangeDataPlan(t *testing.T) {
  6076  	m := testModuleInline(t, map[string]string{
  6077  		"main.tf": `
  6078  data "test_data_source" "foo" {}
  6079  `,
  6080  	})
  6081  
  6082  	p := new(MockProvider)
  6083  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  6084  		DataSources: map[string]*configschema.Block{
  6085  			"test_data_source": {
  6086  				Attributes: map[string]*configschema.Attribute{
  6087  					"id": {
  6088  						Type:     cty.String,
  6089  						Computed: true,
  6090  					},
  6091  					"foo": {
  6092  						Type:     cty.String,
  6093  						Optional: true,
  6094  					},
  6095  				},
  6096  			},
  6097  		},
  6098  	})
  6099  
  6100  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  6101  		State: cty.ObjectVal(map[string]cty.Value{
  6102  			"id":  cty.StringVal("data_id"),
  6103  			"foo": cty.StringVal("foo"),
  6104  		}),
  6105  	}
  6106  
  6107  	state := states.NewState()
  6108  	root := state.EnsureModule(addrs.RootModuleInstance)
  6109  	root.SetResourceInstanceCurrent(
  6110  		mustResourceInstanceAddr("data.test_data_source.foo").Resource,
  6111  		&states.ResourceInstanceObjectSrc{
  6112  			Status:    states.ObjectReady,
  6113  			AttrsJSON: []byte(`{"id":"data_id", "foo":"foo"}`),
  6114  		},
  6115  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6116  	)
  6117  
  6118  	ctx := testContext2(t, &ContextOpts{
  6119  		Providers: map[addrs.Provider]providers.Factory{
  6120  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6121  		},
  6122  	})
  6123  
  6124  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6125  	if diags.HasErrors() {
  6126  		t.Fatal(diags.ErrWithWarnings())
  6127  	}
  6128  
  6129  	for _, res := range plan.Changes.Resources {
  6130  		if res.Action != plans.NoOp {
  6131  			t.Fatalf("expected NoOp, got: %q %s", res.Addr, res.Action)
  6132  		}
  6133  	}
  6134  }
  6135  
  6136  // for_each can reference a resource with 0 instances
  6137  func TestContext2Plan_scaleInForEach(t *testing.T) {
  6138  	p := testProvider("test")
  6139  
  6140  	m := testModuleInline(t, map[string]string{
  6141  		"main.tf": `
  6142  locals {
  6143    m = {}
  6144  }
  6145  
  6146  resource "test_instance" "a" {
  6147    for_each = local.m
  6148  }
  6149  
  6150  resource "test_instance" "b" {
  6151    for_each = test_instance.a
  6152  }
  6153  `})
  6154  
  6155  	state := states.NewState()
  6156  	root := state.EnsureModule(addrs.RootModuleInstance)
  6157  	root.SetResourceInstanceCurrent(
  6158  		mustResourceInstanceAddr("test_instance.a[0]").Resource,
  6159  		&states.ResourceInstanceObjectSrc{
  6160  			Status:       states.ObjectReady,
  6161  			AttrsJSON:    []byte(`{"id":"a0"}`),
  6162  			Dependencies: []addrs.ConfigResource{},
  6163  		},
  6164  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6165  	)
  6166  	root.SetResourceInstanceCurrent(
  6167  		mustResourceInstanceAddr("test_instance.b").Resource,
  6168  		&states.ResourceInstanceObjectSrc{
  6169  			Status:       states.ObjectReady,
  6170  			AttrsJSON:    []byte(`{"id":"b"}`),
  6171  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_instance.a")},
  6172  		},
  6173  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6174  	)
  6175  
  6176  	ctx := testContext2(t, &ContextOpts{
  6177  		Providers: map[addrs.Provider]providers.Factory{
  6178  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6179  		},
  6180  	})
  6181  
  6182  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6183  	assertNoErrors(t, diags)
  6184  
  6185  	t.Run("test_instance.a[0]", func(t *testing.T) {
  6186  		instAddr := mustResourceInstanceAddr("test_instance.a[0]")
  6187  		change := plan.Changes.ResourceInstance(instAddr)
  6188  		if change == nil {
  6189  			t.Fatalf("no planned change for %s", instAddr)
  6190  		}
  6191  		if got, want := change.PrevRunAddr, instAddr; !want.Equal(got) {
  6192  			t.Errorf("wrong previous run address for %s %s; want %s", instAddr, got, want)
  6193  		}
  6194  		if got, want := change.Action, plans.Delete; got != want {
  6195  			t.Errorf("wrong action for %s %s; want %s", instAddr, got, want)
  6196  		}
  6197  		if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseWrongRepetition; got != want {
  6198  			t.Errorf("wrong action reason for %s %s; want %s", instAddr, got, want)
  6199  		}
  6200  	})
  6201  	t.Run("test_instance.b", func(t *testing.T) {
  6202  		instAddr := mustResourceInstanceAddr("test_instance.b")
  6203  		change := plan.Changes.ResourceInstance(instAddr)
  6204  		if change == nil {
  6205  			t.Fatalf("no planned change for %s", instAddr)
  6206  		}
  6207  		if got, want := change.PrevRunAddr, instAddr; !want.Equal(got) {
  6208  			t.Errorf("wrong previous run address for %s %s; want %s", instAddr, got, want)
  6209  		}
  6210  		if got, want := change.Action, plans.Delete; got != want {
  6211  			t.Errorf("wrong action for %s %s; want %s", instAddr, got, want)
  6212  		}
  6213  		if got, want := change.ActionReason, plans.ResourceInstanceDeleteBecauseWrongRepetition; got != want {
  6214  			t.Errorf("wrong action reason for %s %s; want %s", instAddr, got, want)
  6215  		}
  6216  	})
  6217  }
  6218  
  6219  func TestContext2Plan_targetedModuleInstance(t *testing.T) {
  6220  	m := testModule(t, "plan-targeted")
  6221  	p := testProvider("aws")
  6222  	p.PlanResourceChangeFn = testDiffFn
  6223  	ctx := testContext2(t, &ContextOpts{
  6224  		Providers: map[addrs.Provider]providers.Factory{
  6225  			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
  6226  		},
  6227  	})
  6228  
  6229  	plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
  6230  		Mode: plans.NormalMode,
  6231  		Targets: []addrs.Targetable{
  6232  			addrs.RootModuleInstance.Child("mod", addrs.IntKey(0)),
  6233  		},
  6234  	})
  6235  	if diags.HasErrors() {
  6236  		t.Fatalf("unexpected errors: %s", diags.Err())
  6237  	}
  6238  	schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
  6239  	ty := schema.ImpliedType()
  6240  
  6241  	if len(plan.Changes.Resources) != 1 {
  6242  		t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
  6243  	}
  6244  
  6245  	for _, res := range plan.Changes.Resources {
  6246  		ric, err := res.Decode(ty)
  6247  		if err != nil {
  6248  			t.Fatal(err)
  6249  		}
  6250  
  6251  		switch i := ric.Addr.String(); i {
  6252  		case "module.mod[0].aws_instance.foo":
  6253  			if res.Action != plans.Create {
  6254  				t.Fatalf("resource %s should be created", i)
  6255  			}
  6256  			checkVals(t, objectVal(t, schema, map[string]cty.Value{
  6257  				"id":   cty.UnknownVal(cty.String),
  6258  				"num":  cty.NumberIntVal(2),
  6259  				"type": cty.UnknownVal(cty.String),
  6260  			}), ric.After)
  6261  		default:
  6262  			t.Fatal("unknown instance:", i)
  6263  		}
  6264  	}
  6265  }
  6266  
  6267  func TestContext2Plan_dataRefreshedInPlan(t *testing.T) {
  6268  	m := testModuleInline(t, map[string]string{
  6269  		"main.tf": `
  6270  data "test_data_source" "d" {
  6271  }
  6272  `})
  6273  
  6274  	p := testProvider("test")
  6275  	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
  6276  		State: cty.ObjectVal(map[string]cty.Value{
  6277  			"id":  cty.StringVal("this"),
  6278  			"foo": cty.NullVal(cty.String),
  6279  		}),
  6280  	}
  6281  
  6282  	ctx := testContext2(t, &ContextOpts{
  6283  		Providers: map[addrs.Provider]providers.Factory{
  6284  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6285  		},
  6286  	})
  6287  
  6288  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6289  	if diags.HasErrors() {
  6290  		t.Fatal(diags.ErrWithWarnings())
  6291  	}
  6292  
  6293  	d := plan.PriorState.ResourceInstance(mustResourceInstanceAddr("data.test_data_source.d"))
  6294  	if d == nil || d.Current == nil {
  6295  		t.Fatal("data.test_data_source.d not found in state:", plan.PriorState)
  6296  	}
  6297  
  6298  	if d.Current.Status != states.ObjectReady {
  6299  		t.Fatal("expected data.test_data_source.d to be fully read in refreshed state, got status", d.Current.Status)
  6300  	}
  6301  }
  6302  
  6303  func TestContext2Plan_dataReferencesResourceDirectly(t *testing.T) {
  6304  	// When a data resource refers to a managed resource _directly_, any
  6305  	// pending change for the managed resource will cause the data resource
  6306  	// to be deferred to the apply step.
  6307  	// See also TestContext2Plan_dataReferencesResourceIndirectly for the
  6308  	// other case, where the reference is indirect.
  6309  
  6310  	p := testProvider("test")
  6311  
  6312  	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
  6313  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source should not be read"))
  6314  		return resp
  6315  	}
  6316  
  6317  	m := testModuleInline(t, map[string]string{
  6318  		"main.tf": `
  6319  locals {
  6320    x = "value"
  6321  }
  6322  
  6323  resource "test_resource" "a" {
  6324    value = local.x
  6325  }
  6326  
  6327  // test_resource.a.value can be resolved during plan, but the reference implies
  6328  // that the data source should wait until the resource is created.
  6329  data "test_data_source" "d" {
  6330    foo = test_resource.a.value
  6331  }
  6332  
  6333  // ensure referencing an indexed instance that has not yet created will also
  6334  // delay reading the data source
  6335  resource "test_resource" "b" {
  6336    count = 2
  6337    value = local.x
  6338  }
  6339  
  6340  data "test_data_source" "e" {
  6341    foo = test_resource.b[0].value
  6342  }
  6343  `})
  6344  
  6345  	ctx := testContext2(t, &ContextOpts{
  6346  		Providers: map[addrs.Provider]providers.Factory{
  6347  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6348  		},
  6349  	})
  6350  
  6351  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6352  	assertNoErrors(t, diags)
  6353  
  6354  	rc := plan.Changes.ResourceInstance(addrs.Resource{
  6355  		Mode: addrs.DataResourceMode,
  6356  		Type: "test_data_source",
  6357  		Name: "d",
  6358  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
  6359  	if rc != nil {
  6360  		if got, want := rc.ActionReason, plans.ResourceInstanceReadBecauseDependencyPending; got != want {
  6361  			t.Errorf("wrong ActionReason\ngot:  %s\nwant: %s", got, want)
  6362  		}
  6363  	} else {
  6364  		t.Error("no change for test_data_source.e")
  6365  	}
  6366  }
  6367  
  6368  func TestContext2Plan_dataReferencesResourceIndirectly(t *testing.T) {
  6369  	// When a data resource refers to a managed resource indirectly, pending
  6370  	// changes for the managed resource _do not_ cause the data resource to
  6371  	// be deferred to apply. This is a pragmatic special case added for
  6372  	// backward compatibility with the old situation where we would _always_
  6373  	// eagerly read data resources with known configurations, regardless of
  6374  	// the plans for their dependencies.
  6375  	// This test creates an indirection through a local value, but the same
  6376  	// principle would apply for both input variable and output value
  6377  	// indirection.
  6378  	//
  6379  	// See also TestContext2Plan_dataReferencesResourceDirectly for the
  6380  	// other case, where the reference is direct.
  6381  	// This special exception doesn't apply for a data resource that has
  6382  	// custom conditions; see
  6383  	// TestContext2Plan_dataResourceChecksManagedResourceChange for that
  6384  	// situation.
  6385  
  6386  	p := testProvider("test")
  6387  	var applyCount int64
  6388  	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
  6389  		atomic.AddInt64(&applyCount, 1)
  6390  		resp.NewState = req.PlannedState
  6391  		return resp
  6392  	}
  6393  	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
  6394  		if atomic.LoadInt64(&applyCount) == 0 {
  6395  			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source read before managed resource apply"))
  6396  		} else {
  6397  			resp.State = req.Config
  6398  		}
  6399  		return resp
  6400  	}
  6401  
  6402  	m := testModuleInline(t, map[string]string{
  6403  		"main.tf": `
  6404  locals {
  6405  	x = "value"
  6406  }
  6407  
  6408  resource "test_resource" "a" {
  6409  	value = local.x
  6410  }
  6411  
  6412  locals {
  6413  	y = test_resource.a.value
  6414  }
  6415  
  6416  // test_resource.a.value would ideally cause a pending change for
  6417  // test_resource.a to defer this to the apply step, but we intentionally don't
  6418  // do that when it's indirect (through a local value, here) as a concession
  6419  // to backward compatibility.
  6420  data "test_data_source" "d" {
  6421  	foo = local.y
  6422  }
  6423  `})
  6424  
  6425  	ctx := testContext2(t, &ContextOpts{
  6426  		Providers: map[addrs.Provider]providers.Factory{
  6427  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6428  		},
  6429  	})
  6430  
  6431  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6432  	if !diags.HasErrors() {
  6433  		t.Fatalf("successful plan; want an error")
  6434  	}
  6435  
  6436  	if got, want := diags.Err().Error(), "data source read before managed resource apply"; !strings.Contains(got, want) {
  6437  		t.Errorf("Missing expected error message\ngot: %s\nwant substring: %s", got, want)
  6438  	}
  6439  }
  6440  
  6441  func TestContext2Plan_skipRefresh(t *testing.T) {
  6442  	p := testProvider("test")
  6443  	p.PlanResourceChangeFn = testDiffFn
  6444  
  6445  	m := testModuleInline(t, map[string]string{
  6446  		"main.tf": `
  6447  resource "test_instance" "a" {
  6448  }
  6449  `})
  6450  
  6451  	state := states.NewState()
  6452  	root := state.EnsureModule(addrs.RootModuleInstance)
  6453  	root.SetResourceInstanceCurrent(
  6454  		mustResourceInstanceAddr("test_instance.a").Resource,
  6455  		&states.ResourceInstanceObjectSrc{
  6456  			Status:       states.ObjectReady,
  6457  			AttrsJSON:    []byte(`{"id":"a","type":"test_instance"}`),
  6458  			Dependencies: []addrs.ConfigResource{},
  6459  		},
  6460  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6461  	)
  6462  
  6463  	ctx := testContext2(t, &ContextOpts{
  6464  		Providers: map[addrs.Provider]providers.Factory{
  6465  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6466  		},
  6467  	})
  6468  
  6469  	plan, diags := ctx.Plan(m, state, &PlanOpts{
  6470  		Mode:        plans.NormalMode,
  6471  		SkipRefresh: true,
  6472  	})
  6473  	assertNoErrors(t, diags)
  6474  
  6475  	if p.ReadResourceCalled {
  6476  		t.Fatal("Resource should not have been refreshed")
  6477  	}
  6478  
  6479  	for _, c := range plan.Changes.Resources {
  6480  		if c.Action != plans.NoOp {
  6481  			t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr)
  6482  		}
  6483  	}
  6484  }
  6485  
  6486  func TestContext2Plan_dataInModuleDependsOn(t *testing.T) {
  6487  	p := testProvider("test")
  6488  
  6489  	readDataSourceB := false
  6490  	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
  6491  		cfg := req.Config.AsValueMap()
  6492  		foo := cfg["foo"].AsString()
  6493  
  6494  		cfg["id"] = cty.StringVal("ID")
  6495  		cfg["foo"] = cty.StringVal("new")
  6496  
  6497  		if foo == "b" {
  6498  			readDataSourceB = true
  6499  		}
  6500  
  6501  		resp.State = cty.ObjectVal(cfg)
  6502  		return resp
  6503  	}
  6504  
  6505  	m := testModuleInline(t, map[string]string{
  6506  		"main.tf": `
  6507  module "a" {
  6508    source = "./mod_a"
  6509  }
  6510  
  6511  module "b" {
  6512    source = "./mod_b"
  6513    depends_on = [module.a]
  6514  }`,
  6515  		"mod_a/main.tf": `
  6516  data "test_data_source" "a" {
  6517    foo = "a"
  6518  }`,
  6519  		"mod_b/main.tf": `
  6520  data "test_data_source" "b" {
  6521    foo = "b"
  6522  }`,
  6523  	})
  6524  
  6525  	ctx := testContext2(t, &ContextOpts{
  6526  		Providers: map[addrs.Provider]providers.Factory{
  6527  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6528  		},
  6529  	})
  6530  
  6531  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6532  	assertNoErrors(t, diags)
  6533  
  6534  	// The change to data source a should not prevent data source b from being
  6535  	// read.
  6536  	if !readDataSourceB {
  6537  		t.Fatal("data source b was not read during plan")
  6538  	}
  6539  }
  6540  
  6541  func TestContext2Plan_rpcDiagnostics(t *testing.T) {
  6542  	m := testModuleInline(t, map[string]string{
  6543  		"main.tf": `
  6544  resource "test_instance" "a" {
  6545  }
  6546  `,
  6547  	})
  6548  
  6549  	p := testProvider("test")
  6550  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
  6551  		resp := testDiffFn(req)
  6552  		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble"))
  6553  		return resp
  6554  	}
  6555  
  6556  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  6557  		ResourceTypes: map[string]*configschema.Block{
  6558  			"test_instance": {
  6559  				Attributes: map[string]*configschema.Attribute{
  6560  					"id": {Type: cty.String, Computed: true},
  6561  				},
  6562  			},
  6563  		},
  6564  	})
  6565  
  6566  	ctx := testContext2(t, &ContextOpts{
  6567  		Providers: map[addrs.Provider]providers.Factory{
  6568  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6569  		},
  6570  	})
  6571  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6572  	if diags.HasErrors() {
  6573  		t.Fatal(diags.Err())
  6574  	}
  6575  
  6576  	if len(diags) == 0 {
  6577  		t.Fatal("expected warnings")
  6578  	}
  6579  
  6580  	for _, d := range diags {
  6581  		des := d.Description().Summary
  6582  		if !strings.Contains(des, "frobble") {
  6583  			t.Fatalf(`expected frobble, got %q`, des)
  6584  		}
  6585  	}
  6586  }
  6587  
  6588  // ignore_changes needs to be re-applied to the planned value for provider
  6589  // using the LegacyTypeSystem
  6590  func TestContext2Plan_legacyProviderIgnoreChanges(t *testing.T) {
  6591  	m := testModuleInline(t, map[string]string{
  6592  		"main.tf": `
  6593  resource "test_instance" "a" {
  6594    lifecycle {
  6595      ignore_changes = [data]
  6596    }
  6597  }
  6598  `,
  6599  	})
  6600  
  6601  	p := testProvider("test")
  6602  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  6603  		m := req.ProposedNewState.AsValueMap()
  6604  		// this provider "hashes" the data attribute as bar
  6605  		m["data"] = cty.StringVal("bar")
  6606  
  6607  		resp.PlannedState = cty.ObjectVal(m)
  6608  		resp.LegacyTypeSystem = true
  6609  		return resp
  6610  	}
  6611  
  6612  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  6613  		ResourceTypes: map[string]*configschema.Block{
  6614  			"test_instance": {
  6615  				Attributes: map[string]*configschema.Attribute{
  6616  					"id":   {Type: cty.String, Computed: true},
  6617  					"data": {Type: cty.String, Optional: true},
  6618  				},
  6619  			},
  6620  		},
  6621  	})
  6622  
  6623  	state := states.NewState()
  6624  	root := state.EnsureModule(addrs.RootModuleInstance)
  6625  	root.SetResourceInstanceCurrent(
  6626  		mustResourceInstanceAddr("test_instance.a").Resource,
  6627  		&states.ResourceInstanceObjectSrc{
  6628  			Status:       states.ObjectReady,
  6629  			AttrsJSON:    []byte(`{"id":"a","data":"foo"}`),
  6630  			Dependencies: []addrs.ConfigResource{},
  6631  		},
  6632  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6633  	)
  6634  
  6635  	ctx := testContext2(t, &ContextOpts{
  6636  		Providers: map[addrs.Provider]providers.Factory{
  6637  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6638  		},
  6639  	})
  6640  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6641  	if diags.HasErrors() {
  6642  		t.Fatal(diags.Err())
  6643  	}
  6644  
  6645  	for _, c := range plan.Changes.Resources {
  6646  		if c.Action != plans.NoOp {
  6647  			t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr)
  6648  		}
  6649  	}
  6650  }
  6651  
  6652  func TestContext2Plan_validateIgnoreAll(t *testing.T) {
  6653  	m := testModuleInline(t, map[string]string{
  6654  		"main.tf": `
  6655  resource "test_instance" "a" {
  6656    lifecycle {
  6657      ignore_changes = all
  6658    }
  6659  }
  6660  `,
  6661  	})
  6662  
  6663  	p := testProvider("test")
  6664  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  6665  		ResourceTypes: map[string]*configschema.Block{
  6666  			"test_instance": {
  6667  				Attributes: map[string]*configschema.Attribute{
  6668  					"id":   {Type: cty.String, Computed: true},
  6669  					"data": {Type: cty.String, Optional: true},
  6670  				},
  6671  			},
  6672  		},
  6673  	})
  6674  	p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
  6675  		var diags tfdiags.Diagnostics
  6676  		if req.TypeName == "test_instance" {
  6677  			if !req.Config.GetAttr("id").IsNull() {
  6678  				diags = diags.Append(errors.New("id cannot be set in config"))
  6679  			}
  6680  		}
  6681  		return providers.ValidateResourceConfigResponse{
  6682  			Diagnostics: diags,
  6683  		}
  6684  	}
  6685  
  6686  	state := states.NewState()
  6687  	root := state.EnsureModule(addrs.RootModuleInstance)
  6688  	root.SetResourceInstanceCurrent(
  6689  		mustResourceInstanceAddr("test_instance.a").Resource,
  6690  		&states.ResourceInstanceObjectSrc{
  6691  			Status:       states.ObjectReady,
  6692  			AttrsJSON:    []byte(`{"id":"a","data":"foo"}`),
  6693  			Dependencies: []addrs.ConfigResource{},
  6694  		},
  6695  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6696  	)
  6697  
  6698  	ctx := testContext2(t, &ContextOpts{
  6699  		Providers: map[addrs.Provider]providers.Factory{
  6700  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6701  		},
  6702  	})
  6703  	_, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6704  	if diags.HasErrors() {
  6705  		t.Fatal(diags.Err())
  6706  	}
  6707  }
  6708  
  6709  func TestContext2Plan_legacyProviderIgnoreAll(t *testing.T) {
  6710  	m := testModuleInline(t, map[string]string{
  6711  		"main.tf": `
  6712  resource "test_instance" "a" {
  6713    lifecycle {
  6714      ignore_changes = all
  6715    }
  6716    data = "foo"
  6717  }
  6718  `,
  6719  	})
  6720  
  6721  	p := testProvider("test")
  6722  	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
  6723  		ResourceTypes: map[string]*configschema.Block{
  6724  			"test_instance": {
  6725  				Attributes: map[string]*configschema.Attribute{
  6726  					"id":   {Type: cty.String, Computed: true},
  6727  					"data": {Type: cty.String, Optional: true},
  6728  				},
  6729  			},
  6730  		},
  6731  	})
  6732  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
  6733  		plan := req.ProposedNewState.AsValueMap()
  6734  		// Update both the computed id and the configured data.
  6735  		// Legacy providers expect tofu to be able to ignore these.
  6736  
  6737  		plan["id"] = cty.StringVal("updated")
  6738  		plan["data"] = cty.StringVal("updated")
  6739  		resp.PlannedState = cty.ObjectVal(plan)
  6740  		resp.LegacyTypeSystem = true
  6741  		return resp
  6742  	}
  6743  
  6744  	state := states.NewState()
  6745  	root := state.EnsureModule(addrs.RootModuleInstance)
  6746  	root.SetResourceInstanceCurrent(
  6747  		mustResourceInstanceAddr("test_instance.a").Resource,
  6748  		&states.ResourceInstanceObjectSrc{
  6749  			Status:       states.ObjectReady,
  6750  			AttrsJSON:    []byte(`{"id":"orig","data":"orig"}`),
  6751  			Dependencies: []addrs.ConfigResource{},
  6752  		},
  6753  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6754  	)
  6755  
  6756  	ctx := testContext2(t, &ContextOpts{
  6757  		Providers: map[addrs.Provider]providers.Factory{
  6758  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6759  		},
  6760  	})
  6761  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6762  	if diags.HasErrors() {
  6763  		t.Fatal(diags.Err())
  6764  	}
  6765  
  6766  	for _, c := range plan.Changes.Resources {
  6767  		if c.Action != plans.NoOp {
  6768  			t.Fatalf("expected NoOp plan, got %s\n", c.Action)
  6769  		}
  6770  	}
  6771  }
  6772  
  6773  func TestContext2Plan_dataRemovalNoProvider(t *testing.T) {
  6774  	m := testModuleInline(t, map[string]string{
  6775  		"main.tf": `
  6776  resource "test_instance" "a" {
  6777  }
  6778  `,
  6779  	})
  6780  
  6781  	p := testProvider("test")
  6782  
  6783  	state := states.NewState()
  6784  	root := state.EnsureModule(addrs.RootModuleInstance)
  6785  	root.SetResourceInstanceCurrent(
  6786  		mustResourceInstanceAddr("test_instance.a").Resource,
  6787  		&states.ResourceInstanceObjectSrc{
  6788  			Status:       states.ObjectReady,
  6789  			AttrsJSON:    []byte(`{"id":"a","data":"foo"}`),
  6790  			Dependencies: []addrs.ConfigResource{},
  6791  		},
  6792  		mustProviderConfig(`provider["registry.opentofu.org/hashicorp/test"]`),
  6793  	)
  6794  
  6795  	// the provider for this data source is no longer in the config, but that
  6796  	// should not matter for state removal.
  6797  	root.SetResourceInstanceCurrent(
  6798  		mustResourceInstanceAddr("data.test_data_source.d").Resource,
  6799  		&states.ResourceInstanceObjectSrc{
  6800  			Status:       states.ObjectReady,
  6801  			AttrsJSON:    []byte(`{"id":"d"}`),
  6802  			Dependencies: []addrs.ConfigResource{},
  6803  		},
  6804  		mustProviderConfig(`provider["registry.opentofu.org/local/test"]`),
  6805  	)
  6806  
  6807  	ctx := testContext2(t, &ContextOpts{
  6808  		Providers: map[addrs.Provider]providers.Factory{
  6809  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6810  			// We still need to be able to locate the provider to decode the
  6811  			// state, since we do not know during init that this provider is
  6812  			// only used for an orphaned data source.
  6813  			addrs.NewProvider("registry.opentofu.org", "local", "test"): testProviderFuncFixed(p),
  6814  		},
  6815  	})
  6816  	_, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6817  	if diags.HasErrors() {
  6818  		t.Fatal(diags.Err())
  6819  	}
  6820  }
  6821  
  6822  func TestContext2Plan_noSensitivityChange(t *testing.T) {
  6823  	m := testModuleInline(t, map[string]string{
  6824  		"main.tf": `
  6825  variable "sensitive_var" {
  6826         default = "hello"
  6827         sensitive = true
  6828  }
  6829  
  6830  resource "test_resource" "foo" {
  6831         value = var.sensitive_var
  6832         sensitive_value = var.sensitive_var
  6833  }`,
  6834  	})
  6835  
  6836  	p := testProvider("test")
  6837  
  6838  	ctx := testContext2(t, &ContextOpts{
  6839  		Providers: map[addrs.Provider]providers.Factory{
  6840  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6841  		},
  6842  	})
  6843  	state := states.BuildState(func(s *states.SyncState) {
  6844  		s.SetResourceInstanceCurrent(
  6845  			addrs.Resource{
  6846  				Mode: addrs.ManagedResourceMode,
  6847  				Type: "test_resource",
  6848  				Name: "foo",
  6849  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
  6850  			&states.ResourceInstanceObjectSrc{
  6851  				Status:    states.ObjectReady,
  6852  				AttrsJSON: []byte(`{"id":"foo", "value":"hello", "sensitive_value":"hello"}`),
  6853  				AttrSensitivePaths: []cty.PathValueMarks{
  6854  					{Path: cty.Path{cty.GetAttrStep{Name: "value"}}, Marks: cty.NewValueMarks(marks.Sensitive)},
  6855  					{Path: cty.Path{cty.GetAttrStep{Name: "sensitive_value"}}, Marks: cty.NewValueMarks(marks.Sensitive)},
  6856  				},
  6857  			},
  6858  			addrs.AbsProviderConfig{
  6859  				Provider: addrs.NewDefaultProvider("test"),
  6860  				Module:   addrs.RootModule,
  6861  			},
  6862  		)
  6863  	})
  6864  	plan, diags := ctx.Plan(m, state, SimplePlanOpts(plans.NormalMode, testInputValuesUnset(m.Module.Variables)))
  6865  	if diags.HasErrors() {
  6866  		t.Fatal(diags.Err())
  6867  	}
  6868  
  6869  	for _, c := range plan.Changes.Resources {
  6870  		if c.Action != plans.NoOp {
  6871  			t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr)
  6872  		}
  6873  	}
  6874  }
  6875  
  6876  func TestContext2Plan_variableCustomValidationsSensitive(t *testing.T) {
  6877  	m := testModule(t, "validate-variable-custom-validations-child-sensitive")
  6878  
  6879  	p := testProvider("test")
  6880  	ctx := testContext2(t, &ContextOpts{
  6881  		Providers: map[addrs.Provider]providers.Factory{
  6882  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
  6883  		},
  6884  	})
  6885  
  6886  	_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6887  	if !diags.HasErrors() {
  6888  		t.Fatal("succeeded; want errors")
  6889  	}
  6890  	if got, want := diags.Err().Error(), `Invalid value for variable: Value must not be "nope".`; !strings.Contains(got, want) {
  6891  		t.Fatalf("wrong error:\ngot:  %s\nwant: message containing %q", got, want)
  6892  	}
  6893  }
  6894  
  6895  func TestContext2Plan_nullOutputNoOp(t *testing.T) {
  6896  	// this should always plan a NoOp change for the output
  6897  	m := testModuleInline(t, map[string]string{
  6898  		"main.tf": `
  6899  output "planned" {
  6900    value = false ? 1 : null
  6901  }
  6902  `,
  6903  	})
  6904  
  6905  	ctx := testContext2(t, &ContextOpts{})
  6906  	state := states.BuildState(func(s *states.SyncState) {
  6907  		r := s.Module(addrs.RootModuleInstance)
  6908  		r.SetOutputValue("planned", cty.NullVal(cty.DynamicPseudoType), false)
  6909  	})
  6910  	plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
  6911  	if diags.HasErrors() {
  6912  		t.Fatal(diags.Err())
  6913  	}
  6914  
  6915  	for _, c := range plan.Changes.Outputs {
  6916  		if c.Action != plans.NoOp {
  6917  			t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr)
  6918  		}
  6919  	}
  6920  }
  6921  
  6922  func TestContext2Plan_createOutput(t *testing.T) {
  6923  	// this should always plan a NoOp change for the output
  6924  	m := testModuleInline(t, map[string]string{
  6925  		"main.tf": `
  6926  output "planned" {
  6927    value = 1
  6928  }
  6929  `,
  6930  	})
  6931  
  6932  	ctx := testContext2(t, &ContextOpts{})
  6933  	plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
  6934  	if diags.HasErrors() {
  6935  		t.Fatal(diags.Err())
  6936  	}
  6937  
  6938  	for _, c := range plan.Changes.Outputs {
  6939  		if c.Action != plans.Create {
  6940  			t.Fatalf("expected Create change, got %s for %q", c.Action, c.Addr)
  6941  		}
  6942  	}
  6943  }
  6944  
  6945  ////////////////////////////////////////////////////////////////////////////////
  6946  // NOTE: Due to the size of this file, new tests should be added to
  6947  // context_plan2_test.go.
  6948  ////////////////////////////////////////////////////////////////////////////////