github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/node_output_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/terraform/internal/addrs"
    11  	"github.com/hashicorp/terraform/internal/checks"
    12  	"github.com/hashicorp/terraform/internal/configs"
    13  	"github.com/hashicorp/terraform/internal/lang/marks"
    14  	"github.com/hashicorp/terraform/internal/states"
    15  )
    16  
    17  func TestNodeApplyableOutputExecute_knownValue(t *testing.T) {
    18  	ctx := new(MockEvalContext)
    19  	ctx.StateState = states.NewState().SyncWrapper()
    20  	ctx.RefreshStateState = states.NewState().SyncWrapper()
    21  	ctx.ChecksState = checks.NewState(nil)
    22  
    23  	config := &configs.Output{Name: "map-output"}
    24  	addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
    25  	node := &NodeApplyableOutput{Config: config, Addr: addr}
    26  	val := cty.MapVal(map[string]cty.Value{
    27  		"a": cty.StringVal("b"),
    28  	})
    29  	ctx.EvaluateExprResult = val
    30  
    31  	err := node.Execute(ctx, walkApply)
    32  	if err != nil {
    33  		t.Fatalf("unexpected execute error: %s", err)
    34  	}
    35  
    36  	outputVal := ctx.StateState.OutputValue(addr)
    37  	if got, want := outputVal.Value, val; !got.RawEquals(want) {
    38  		t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want)
    39  	}
    40  
    41  	if !ctx.RefreshStateCalled {
    42  		t.Fatal("should have called RefreshState, but didn't")
    43  	}
    44  	refreshOutputVal := ctx.RefreshStateState.OutputValue(addr)
    45  	if got, want := refreshOutputVal.Value, val; !got.RawEquals(want) {
    46  		t.Fatalf("wrong output value in refresh state\n got: %#v\nwant: %#v", got, want)
    47  	}
    48  }
    49  
    50  func TestNodeApplyableOutputExecute_noState(t *testing.T) {
    51  	ctx := new(MockEvalContext)
    52  
    53  	config := &configs.Output{Name: "map-output"}
    54  	addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
    55  	node := &NodeApplyableOutput{Config: config, Addr: addr}
    56  	val := cty.MapVal(map[string]cty.Value{
    57  		"a": cty.StringVal("b"),
    58  	})
    59  	ctx.EvaluateExprResult = val
    60  
    61  	err := node.Execute(ctx, walkApply)
    62  	if err != nil {
    63  		t.Fatalf("unexpected execute error: %s", err)
    64  	}
    65  }
    66  
    67  func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) {
    68  	ctx := new(MockEvalContext)
    69  	ctx.StateState = states.NewState().SyncWrapper()
    70  	ctx.ChecksState = checks.NewState(nil)
    71  
    72  	config := &configs.Output{
    73  		Name: "map-output",
    74  		DependsOn: []hcl.Traversal{
    75  			{
    76  				hcl.TraverseRoot{Name: "test_instance"},
    77  				hcl.TraverseAttr{Name: "foo"},
    78  				hcl.TraverseAttr{Name: "bar"},
    79  			},
    80  		},
    81  	}
    82  	addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
    83  	node := &NodeApplyableOutput{Config: config, Addr: addr}
    84  	val := cty.MapVal(map[string]cty.Value{
    85  		"a": cty.StringVal("b"),
    86  	})
    87  	ctx.EvaluateExprResult = val
    88  
    89  	diags := node.Execute(ctx, walkApply)
    90  	if !diags.HasErrors() {
    91  		t.Fatal("expected execute error, but there was none")
    92  	}
    93  	if got, want := diags.Err().Error(), "Invalid depends_on reference"; !strings.Contains(got, want) {
    94  		t.Errorf("expected error to include %q, but was: %s", want, got)
    95  	}
    96  }
    97  
    98  func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) {
    99  	ctx := new(MockEvalContext)
   100  	ctx.StateState = states.NewState().SyncWrapper()
   101  	ctx.ChecksState = checks.NewState(nil)
   102  
   103  	config := &configs.Output{Name: "map-output"}
   104  	addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
   105  	node := &NodeApplyableOutput{Config: config, Addr: addr}
   106  	val := cty.MapVal(map[string]cty.Value{
   107  		"a": cty.StringVal("b").Mark(marks.Sensitive),
   108  	})
   109  	ctx.EvaluateExprResult = val
   110  
   111  	diags := node.Execute(ctx, walkApply)
   112  	if !diags.HasErrors() {
   113  		t.Fatal("expected execute error, but there was none")
   114  	}
   115  	if got, want := diags.Err().Error(), "Output refers to sensitive values"; !strings.Contains(got, want) {
   116  		t.Errorf("expected error to include %q, but was: %s", want, got)
   117  	}
   118  }
   119  
   120  func TestNodeApplyableOutputExecute_sensitiveValueAndOutput(t *testing.T) {
   121  	ctx := new(MockEvalContext)
   122  	ctx.StateState = states.NewState().SyncWrapper()
   123  	ctx.ChecksState = checks.NewState(nil)
   124  
   125  	config := &configs.Output{
   126  		Name:      "map-output",
   127  		Sensitive: true,
   128  	}
   129  	addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance)
   130  	node := &NodeApplyableOutput{Config: config, Addr: addr}
   131  	val := cty.MapVal(map[string]cty.Value{
   132  		"a": cty.StringVal("b").Mark(marks.Sensitive),
   133  	})
   134  	ctx.EvaluateExprResult = val
   135  
   136  	err := node.Execute(ctx, walkApply)
   137  	if err != nil {
   138  		t.Fatalf("unexpected execute error: %s", err)
   139  	}
   140  
   141  	// Unmarked value should be stored in state
   142  	outputVal := ctx.StateState.OutputValue(addr)
   143  	want, _ := val.UnmarkDeep()
   144  	if got := outputVal.Value; !got.RawEquals(want) {
   145  		t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want)
   146  	}
   147  }
   148  
   149  func TestNodeDestroyableOutputExecute(t *testing.T) {
   150  	outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)
   151  
   152  	state := states.NewState()
   153  	state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false)
   154  	state.OutputValue(outputAddr)
   155  
   156  	ctx := &MockEvalContext{
   157  		StateState: state.SyncWrapper(),
   158  	}
   159  	node := NodeDestroyableOutput{Addr: outputAddr}
   160  
   161  	diags := node.Execute(ctx, walkApply)
   162  	if diags.HasErrors() {
   163  		t.Fatalf("Unexpected error: %s", diags.Err())
   164  	}
   165  	if state.OutputValue(outputAddr) != nil {
   166  		t.Fatal("Unexpected outputs in state after removal")
   167  	}
   168  }
   169  
   170  func TestNodeDestroyableOutputExecute_notInState(t *testing.T) {
   171  	outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)
   172  
   173  	state := states.NewState()
   174  
   175  	ctx := &MockEvalContext{
   176  		StateState: state.SyncWrapper(),
   177  	}
   178  	node := NodeDestroyableOutput{Addr: outputAddr}
   179  
   180  	diags := node.Execute(ctx, walkApply)
   181  	if diags.HasErrors() {
   182  		t.Fatalf("Unexpected error: %s", diags.Err())
   183  	}
   184  	if state.OutputValue(outputAddr) != nil {
   185  		t.Fatal("Unexpected outputs in state after removal")
   186  	}
   187  }