github.com/opentofu/opentofu@v1.7.1/internal/command/views/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 views
     7  
     8  import (
     9  	"testing"
    10  
    11  	"github.com/opentofu/opentofu/internal/addrs"
    12  	"github.com/opentofu/opentofu/internal/command/arguments"
    13  	"github.com/opentofu/opentofu/internal/configs/configschema"
    14  	"github.com/opentofu/opentofu/internal/plans"
    15  	"github.com/opentofu/opentofu/internal/providers"
    16  	"github.com/opentofu/opentofu/internal/terminal"
    17  	"github.com/opentofu/opentofu/internal/tofu"
    18  	"github.com/zclconf/go-cty/cty"
    19  )
    20  
    21  // Ensure that the correct view type and in-automation settings propagate to the
    22  // Operation view.
    23  func TestPlanHuman_operation(t *testing.T) {
    24  	streams, done := terminal.StreamsForTesting(t)
    25  	defer done(t)
    26  	v := NewPlan(arguments.ViewHuman, NewView(streams).SetRunningInAutomation(true)).Operation()
    27  	if hv, ok := v.(*OperationHuman); !ok {
    28  		t.Fatalf("unexpected return type %t", v)
    29  	} else if hv.inAutomation != true {
    30  		t.Fatalf("unexpected inAutomation value on Operation view")
    31  	}
    32  }
    33  
    34  // Verify that Hooks includes a UI hook
    35  func TestPlanHuman_hooks(t *testing.T) {
    36  	streams, done := terminal.StreamsForTesting(t)
    37  	defer done(t)
    38  	v := NewPlan(arguments.ViewHuman, NewView(streams).SetRunningInAutomation((true)))
    39  	hooks := v.Hooks()
    40  
    41  	var uiHook *UiHook
    42  	for _, hook := range hooks {
    43  		if ch, ok := hook.(*UiHook); ok {
    44  			uiHook = ch
    45  		}
    46  	}
    47  	if uiHook == nil {
    48  		t.Fatalf("expected Hooks to include a UiHook: %#v", hooks)
    49  	}
    50  }
    51  
    52  // Helper functions to build a trivial test plan, to exercise the plan
    53  // renderer.
    54  func testPlan(t *testing.T) *plans.Plan {
    55  	t.Helper()
    56  
    57  	plannedVal := cty.ObjectVal(map[string]cty.Value{
    58  		"id":  cty.UnknownVal(cty.String),
    59  		"foo": cty.StringVal("bar"),
    60  	})
    61  	priorValRaw, err := plans.NewDynamicValue(cty.NullVal(plannedVal.Type()), plannedVal.Type())
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	plannedValRaw, err := plans.NewDynamicValue(plannedVal, plannedVal.Type())
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  
    70  	changes := plans.NewChanges()
    71  	addr := addrs.Resource{
    72  		Mode: addrs.ManagedResourceMode,
    73  		Type: "test_resource",
    74  		Name: "foo",
    75  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
    76  
    77  	changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{
    78  		Addr:        addr,
    79  		PrevRunAddr: addr,
    80  		ProviderAddr: addrs.AbsProviderConfig{
    81  			Provider: addrs.NewDefaultProvider("test"),
    82  			Module:   addrs.RootModule,
    83  		},
    84  		ChangeSrc: plans.ChangeSrc{
    85  			Action: plans.Create,
    86  			Before: priorValRaw,
    87  			After:  plannedValRaw,
    88  		},
    89  	})
    90  
    91  	return &plans.Plan{
    92  		Changes: changes,
    93  	}
    94  }
    95  
    96  func testPlanWithDatasource(t *testing.T) *plans.Plan {
    97  	plan := testPlan(t)
    98  
    99  	addr := addrs.Resource{
   100  		Mode: addrs.DataResourceMode,
   101  		Type: "test_data_source",
   102  		Name: "bar",
   103  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
   104  
   105  	dataVal := cty.ObjectVal(map[string]cty.Value{
   106  		"id":  cty.StringVal("C6743020-40BD-4591-81E6-CD08494341D3"),
   107  		"bar": cty.StringVal("foo"),
   108  	})
   109  	priorValRaw, err := plans.NewDynamicValue(cty.NullVal(dataVal.Type()), dataVal.Type())
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  	plannedValRaw, err := plans.NewDynamicValue(dataVal, dataVal.Type())
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	plan.Changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{
   119  		Addr:        addr,
   120  		PrevRunAddr: addr,
   121  		ProviderAddr: addrs.AbsProviderConfig{
   122  			Provider: addrs.NewDefaultProvider("test"),
   123  			Module:   addrs.RootModule,
   124  		},
   125  		ChangeSrc: plans.ChangeSrc{
   126  			Action: plans.Read,
   127  			Before: priorValRaw,
   128  			After:  plannedValRaw,
   129  		},
   130  	})
   131  
   132  	return plan
   133  }
   134  
   135  func testSchemas() *tofu.Schemas {
   136  	provider := testProvider()
   137  	return &tofu.Schemas{
   138  		Providers: map[addrs.Provider]providers.ProviderSchema{
   139  			addrs.NewDefaultProvider("test"): provider.GetProviderSchema(),
   140  		},
   141  	}
   142  }
   143  
   144  func testProvider() *tofu.MockProvider {
   145  	p := new(tofu.MockProvider)
   146  	p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
   147  		return providers.ReadResourceResponse{NewState: req.PriorState}
   148  	}
   149  
   150  	p.GetProviderSchemaResponse = testProviderSchema()
   151  
   152  	return p
   153  }
   154  
   155  func testProviderSchema() *providers.GetProviderSchemaResponse {
   156  	return &providers.GetProviderSchemaResponse{
   157  		Provider: providers.Schema{
   158  			Block: &configschema.Block{},
   159  		},
   160  		ResourceTypes: map[string]providers.Schema{
   161  			"test_resource": {
   162  				Block: &configschema.Block{
   163  					Attributes: map[string]*configschema.Attribute{
   164  						"id":  {Type: cty.String, Computed: true},
   165  						"foo": {Type: cty.String, Optional: true},
   166  					},
   167  				},
   168  			},
   169  		},
   170  		DataSources: map[string]providers.Schema{
   171  			"test_data_source": {
   172  				Block: &configschema.Block{
   173  					Attributes: map[string]*configschema.Attribute{
   174  						"id":  {Type: cty.String, Required: true},
   175  						"bar": {Type: cty.String, Optional: true},
   176  					},
   177  				},
   178  			},
   179  		},
   180  	}
   181  }