| package terraform |
| |
| import ( |
| "strings" |
| "testing" |
| |
| "github.com/hashicorp/hcl/v2" |
| "github.com/zclconf/go-cty/cty" |
| |
| "github.com/hashicorp/terraform/internal/addrs" |
| "github.com/hashicorp/terraform/internal/checks" |
| "github.com/hashicorp/terraform/internal/configs" |
| "github.com/hashicorp/terraform/internal/lang/marks" |
| "github.com/hashicorp/terraform/internal/states" |
| ) |
| |
| func TestNodeApplyableOutputExecute_knownValue(t *testing.T) { |
| ctx := new(MockEvalContext) |
| ctx.StateState = states.NewState().SyncWrapper() |
| ctx.RefreshStateState = states.NewState().SyncWrapper() |
| ctx.ChecksState = checks.NewState(nil) |
| |
| config := &configs.Output{Name: "map-output"} |
| addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) |
| node := &NodeApplyableOutput{Config: config, Addr: addr} |
| val := cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("b"), |
| }) |
| ctx.EvaluateExprResult = val |
| |
| err := node.Execute(ctx, walkApply) |
| if err != nil { |
| t.Fatalf("unexpected execute error: %s", err) |
| } |
| |
| outputVal := ctx.StateState.OutputValue(addr) |
| if got, want := outputVal.Value, val; !got.RawEquals(want) { |
| t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want) |
| } |
| |
| if !ctx.RefreshStateCalled { |
| t.Fatal("should have called RefreshState, but didn't") |
| } |
| refreshOutputVal := ctx.RefreshStateState.OutputValue(addr) |
| if got, want := refreshOutputVal.Value, val; !got.RawEquals(want) { |
| t.Fatalf("wrong output value in refresh state\n got: %#v\nwant: %#v", got, want) |
| } |
| } |
| |
| func TestNodeApplyableOutputExecute_noState(t *testing.T) { |
| ctx := new(MockEvalContext) |
| |
| config := &configs.Output{Name: "map-output"} |
| addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) |
| node := &NodeApplyableOutput{Config: config, Addr: addr} |
| val := cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("b"), |
| }) |
| ctx.EvaluateExprResult = val |
| |
| err := node.Execute(ctx, walkApply) |
| if err != nil { |
| t.Fatalf("unexpected execute error: %s", err) |
| } |
| } |
| |
| func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) { |
| ctx := new(MockEvalContext) |
| ctx.StateState = states.NewState().SyncWrapper() |
| ctx.ChecksState = checks.NewState(nil) |
| |
| config := &configs.Output{ |
| Name: "map-output", |
| DependsOn: []hcl.Traversal{ |
| { |
| hcl.TraverseRoot{Name: "test_instance"}, |
| hcl.TraverseAttr{Name: "foo"}, |
| hcl.TraverseAttr{Name: "bar"}, |
| }, |
| }, |
| } |
| addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) |
| node := &NodeApplyableOutput{Config: config, Addr: addr} |
| val := cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("b"), |
| }) |
| ctx.EvaluateExprResult = val |
| |
| diags := node.Execute(ctx, walkApply) |
| if !diags.HasErrors() { |
| t.Fatal("expected execute error, but there was none") |
| } |
| if got, want := diags.Err().Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { |
| t.Errorf("expected error to include %q, but was: %s", want, got) |
| } |
| } |
| |
| func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) { |
| ctx := new(MockEvalContext) |
| ctx.StateState = states.NewState().SyncWrapper() |
| ctx.ChecksState = checks.NewState(nil) |
| |
| config := &configs.Output{Name: "map-output"} |
| addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) |
| node := &NodeApplyableOutput{Config: config, Addr: addr} |
| val := cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("b").Mark(marks.Sensitive), |
| }) |
| ctx.EvaluateExprResult = val |
| |
| diags := node.Execute(ctx, walkApply) |
| if !diags.HasErrors() { |
| t.Fatal("expected execute error, but there was none") |
| } |
| if got, want := diags.Err().Error(), "Output refers to sensitive values"; !strings.Contains(got, want) { |
| t.Errorf("expected error to include %q, but was: %s", want, got) |
| } |
| } |
| |
| func TestNodeApplyableOutputExecute_sensitiveValueAndOutput(t *testing.T) { |
| ctx := new(MockEvalContext) |
| ctx.StateState = states.NewState().SyncWrapper() |
| ctx.ChecksState = checks.NewState(nil) |
| |
| config := &configs.Output{ |
| Name: "map-output", |
| Sensitive: true, |
| } |
| addr := addrs.OutputValue{Name: config.Name}.Absolute(addrs.RootModuleInstance) |
| node := &NodeApplyableOutput{Config: config, Addr: addr} |
| val := cty.MapVal(map[string]cty.Value{ |
| "a": cty.StringVal("b").Mark(marks.Sensitive), |
| }) |
| ctx.EvaluateExprResult = val |
| |
| err := node.Execute(ctx, walkApply) |
| if err != nil { |
| t.Fatalf("unexpected execute error: %s", err) |
| } |
| |
| // Unmarked value should be stored in state |
| outputVal := ctx.StateState.OutputValue(addr) |
| want, _ := val.UnmarkDeep() |
| if got := outputVal.Value; !got.RawEquals(want) { |
| t.Errorf("wrong output value in state\n got: %#v\nwant: %#v", got, want) |
| } |
| } |
| |
| func TestNodeDestroyableOutputExecute(t *testing.T) { |
| outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance) |
| |
| state := states.NewState() |
| state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false) |
| state.OutputValue(outputAddr) |
| |
| ctx := &MockEvalContext{ |
| StateState: state.SyncWrapper(), |
| } |
| node := NodeDestroyableOutput{Addr: outputAddr} |
| |
| diags := node.Execute(ctx, walkApply) |
| if diags.HasErrors() { |
| t.Fatalf("Unexpected error: %s", diags.Err()) |
| } |
| if state.OutputValue(outputAddr) != nil { |
| t.Fatal("Unexpected outputs in state after removal") |
| } |
| } |
| |
| func TestNodeDestroyableOutputExecute_notInState(t *testing.T) { |
| outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance) |
| |
| state := states.NewState() |
| |
| ctx := &MockEvalContext{ |
| StateState: state.SyncWrapper(), |
| } |
| node := NodeDestroyableOutput{Addr: outputAddr} |
| |
| diags := node.Execute(ctx, walkApply) |
| if diags.HasErrors() { |
| t.Fatalf("Unexpected error: %s", diags.Err()) |
| } |
| if state.OutputValue(outputAddr) != nil { |
| t.Fatal("Unexpected outputs in state after removal") |
| } |
| } |