github.com/opentofu/opentofu@v1.7.1/internal/tofu/context_eval_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  	"testing"
    10  
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/hcl/v2/hclsyntax"
    13  	"github.com/opentofu/opentofu/internal/addrs"
    14  	"github.com/opentofu/opentofu/internal/providers"
    15  	"github.com/opentofu/opentofu/internal/states"
    16  	"github.com/zclconf/go-cty/cty"
    17  )
    18  
    19  func TestContextEval(t *testing.T) {
    20  	// This test doesn't check the "Want" value for impure funcs, so the value
    21  	// on those doesn't matter.
    22  	tests := []struct {
    23  		Input      string
    24  		Want       cty.Value
    25  		ImpureFunc bool
    26  	}{
    27  		{ // An impure function: allowed in the console, but the result is nondeterministic
    28  			`bcrypt("example")`,
    29  			cty.NilVal,
    30  			true,
    31  		},
    32  		{
    33  			`keys(var.map)`,
    34  			cty.ListVal([]cty.Value{
    35  				cty.StringVal("foo"),
    36  				cty.StringVal("baz"),
    37  			}),
    38  			true,
    39  		},
    40  		{
    41  			`local.result`,
    42  			cty.NumberIntVal(6),
    43  			false,
    44  		},
    45  		{
    46  			`module.child.result`,
    47  			cty.NumberIntVal(6),
    48  			false,
    49  		},
    50  	}
    51  
    52  	// This module has a little bit of everything (and if it is missing somehitng, add to it):
    53  	// resources, variables, locals, modules, output
    54  	m := testModule(t, "eval-context-basic")
    55  	p := testProvider("test")
    56  	ctx := testContext2(t, &ContextOpts{
    57  		Providers: map[addrs.Provider]providers.Factory{
    58  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
    59  		},
    60  	})
    61  
    62  	scope, diags := ctx.Eval(m, states.NewState(), addrs.RootModuleInstance, &EvalOpts{
    63  		SetVariables: testInputValuesUnset(m.Module.Variables),
    64  	})
    65  	if diags.HasErrors() {
    66  		t.Fatalf("Eval errors: %s", diags.Err())
    67  	}
    68  
    69  	// Since we're testing 'eval' (used by tofu console), impure functions
    70  	// should be allowed by the scope.
    71  	if scope.PureOnly == true {
    72  		t.Fatal("wrong result: eval should allow impure funcs")
    73  	}
    74  
    75  	for _, test := range tests {
    76  		t.Run(test.Input, func(t *testing.T) {
    77  			// Parse the test input as an expression
    78  			expr, _ := hclsyntax.ParseExpression([]byte(test.Input), "<test-input>", hcl.Pos{Line: 1, Column: 1})
    79  			got, diags := scope.EvalExpr(expr, cty.DynamicPseudoType)
    80  
    81  			if diags.HasErrors() {
    82  				t.Fatalf("unexpected error: %s", diags.Err())
    83  			}
    84  
    85  			if !test.ImpureFunc {
    86  				if !got.RawEquals(test.Want) {
    87  					t.Fatalf("wrong result: want %#v, got %#v", test.Want, got)
    88  				}
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  // ensure that we can execute a console when outputs have preconditions
    95  func TestContextEval_outputsWithPreconditions(t *testing.T) {
    96  	m := testModuleInline(t, map[string]string{
    97  		"main.tf": `
    98  module "mod" {
    99    source = "./mod"
   100    input  = "ok"
   101  }
   102  
   103  output "out" {
   104    value = module.mod.out
   105  }
   106  `,
   107  
   108  		"./mod/main.tf": `
   109  variable "input" {
   110    type = string
   111  }
   112  
   113  output "out" {
   114    value = var.input
   115  
   116    precondition {
   117      condition     = var.input != ""
   118      error_message = "error"
   119    }
   120  }
   121  `,
   122  	})
   123  
   124  	p := simpleMockProvider()
   125  	ctx := testContext2(t, &ContextOpts{
   126  		Providers: map[addrs.Provider]providers.Factory{
   127  			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
   128  		},
   129  	})
   130  
   131  	_, diags := ctx.Eval(m, states.NewState(), addrs.RootModuleInstance, &EvalOpts{
   132  		SetVariables: testInputValuesUnset(m.Module.Variables),
   133  	})
   134  	assertNoErrors(t, diags)
   135  }