github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/views/plan_test.go (about)

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