blob: ac828dbcc424f2105ce7297ff9237325a8b6a6b9 [file] [log] [blame]
package terraform
import (
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/states"
"github.com/zclconf/go-cty/cty"
)
func TestContextEval(t *testing.T) {
// This test doesn't check the "Want" value for impure funcs, so the value
// on those doesn't matter.
tests := []struct {
Input string
Want cty.Value
ImpureFunc bool
}{
{ // An impure function: allowed in the console, but the result is nondeterministic
`bcrypt("example")`,
cty.NilVal,
true,
},
{
`keys(var.map)`,
cty.ListVal([]cty.Value{
cty.StringVal("foo"),
cty.StringVal("baz"),
}),
true,
},
{
`local.result`,
cty.NumberIntVal(6),
false,
},
{
`module.child.result`,
cty.NumberIntVal(6),
false,
},
}
// This module has a little bit of everything (and if it is missing somehitng, add to it):
// resources, variables, locals, modules, output
m := testModule(t, "eval-context-basic")
p := testProvider("test")
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
scope, diags := ctx.Eval(m, states.NewState(), addrs.RootModuleInstance, &EvalOpts{
SetVariables: testInputValuesUnset(m.Module.Variables),
})
if diags.HasErrors() {
t.Fatalf("Eval errors: %s", diags.Err())
}
// Since we're testing 'eval' (used by terraform console), impure functions
// should be allowed by the scope.
if scope.PureOnly == true {
t.Fatal("wrong result: eval should allow impure funcs")
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
// Parse the test input as an expression
expr, _ := hclsyntax.ParseExpression([]byte(test.Input), "<test-input>", hcl.Pos{Line: 1, Column: 1})
got, diags := scope.EvalExpr(expr, cty.DynamicPseudoType)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err())
}
if !test.ImpureFunc {
if !got.RawEquals(test.Want) {
t.Fatalf("wrong result: want %#v, got %#v", test.Want, got)
}
}
})
}
}
// ensure that we can execute a console when outputs have preconditions
func TestContextEval_outputsWithPreconditions(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
module "mod" {
source = "./mod"
input = "ok"
}
output "out" {
value = module.mod.out
}
`,
"./mod/main.tf": `
variable "input" {
type = string
}
output "out" {
value = var.input
precondition {
condition = var.input != ""
error_message = "error"
}
}
`,
})
p := simpleMockProvider()
ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
_, diags := ctx.Eval(m, states.NewState(), addrs.RootModuleInstance, &EvalOpts{
SetVariables: testInputValuesUnset(m.Module.Variables),
})
assertNoErrors(t, diags)
}