pulumi/pkg/codegen/hcl2/rewrite_apply_test.go
Pat Gavlin 00fbbb6544
[codegen/hcl2] Fix nested apply rewrites (#5013)
Rewrites that should produce nested applies due to functions that return
eventual types were instead producing a single top-level apply. These
changes fix that by considering a function that produces an eventual
value as inspecting eventual values.
2020-07-16 10:26:10 -07:00

181 lines
5.2 KiB
Go

package hcl2
import (
"fmt"
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/model"
"github.com/pulumi/pulumi/pkg/v2/codegen/hcl2/syntax"
"github.com/stretchr/testify/assert"
)
type nameInfo int
func (nameInfo) Format(name string) string {
return name
}
//nolint: lll
func TestApplyRewriter(t *testing.T) {
cases := []struct {
input, output string
skipPromises bool
}{
{
input: `"v: ${resource.foo.bar}"`,
output: `__apply(resource.foo,eval(foo, "v: ${foo.bar}"))`,
},
{
input: `"v: ${resource.baz[0]}"`,
output: `__apply(resource.baz,eval(baz, "v: ${baz[0]}"))`,
},
{
input: `"v: ${resources[0].foo.bar}"`,
output: `__apply(resources[0].foo,eval(foo, "v: ${foo.bar}"))`,
},
{
input: `"v: ${resources.*.id[0]}"`,
output: `__apply(resources.*.id[0],eval(id, "v: ${id}"))`,
},
{
input: `"v: ${element(resources.*.id, 0)}"`,
output: `__apply(element(resources.*.id, 0),eval(ids, "v: ${ids}"))`,
},
{
input: `"v: ${[for r in resources: r.id][0]}"`,
output: `__apply([for r in resources: r.id][0],eval(id, "v: ${id}"))`,
},
{
input: `"v: ${element([for r in resources: r.id], 0)}"`,
output: `__apply(element([for r in resources: r.id], 0),eval(ids, "v: ${ids}"))`,
},
{
input: `"v: ${resource[key]}"`,
output: `__apply(resource[key],eval(key, "v: ${key}"))`,
},
{
input: `"v: ${resource[resource.id]}"`,
output: `__apply(__apply(resource.id,eval(id, resource[id])),eval(id, "v: ${id}"))`,
},
{
input: `resourcesPromise.*.id`,
output: `__apply(resourcesPromise, eval(resourcesPromise, resourcesPromise.*.id))`,
},
{
input: `[for r in resourcesPromise: r.id]`,
output: `__apply(resourcesPromise,eval(resourcesPromise, [for r in resourcesPromise: r.id]))`,
},
{
input: `resourcesOutput.*.id`,
output: `__apply(resourcesOutput, eval(resourcesOutput, resourcesOutput.*.id))`,
},
{
input: `[for r in resourcesOutput: r.id]`,
output: `__apply(resourcesOutput,eval(resourcesOutput, [for r in resourcesOutput: r.id]))`,
},
{
input: `"v: ${[for r in resourcesPromise: r.id]}"`,
output: `__apply(__apply(resourcesPromise,eval(resourcesPromise, [for r in resourcesPromise: r.id])),eval(ids, "v: ${ids}"))`,
},
{
input: `toJSON({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = "*"
Action = [ "s3:GetObject" ]
Resource = [ "arn:aws:s3:::${resource.id}/*" ]
}]
})`,
output: `__apply(resource.id,eval(id, toJSON({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = "*"
Action = [ "s3:GetObject" ]
Resource = [ "arn:aws:s3:::${id}/*" ]
}]
})))`,
},
{
input: `getPromise().property`,
output: `__apply(getPromise(), eval(getPromise, getPromise.property))`,
},
{
input: `getPromise().object.foo`,
output: `__apply(getPromise(), eval(getPromise, getPromise.object.foo))`,
},
{
input: `getPromise().property`,
output: `getPromise().property`,
skipPromises: true,
},
{
input: `getPromise().object.foo`,
output: `getPromise().object.foo`,
skipPromises: true,
},
{
input: `getPromise(resource.id).property`,
output: `__apply(__apply(resource.id,eval(id, getPromise(id))), eval(getPromise, getPromise.property))`,
},
}
resourceType := model.NewObjectType(map[string]model.Type{
"id": model.NewOutputType(model.StringType),
"foo": model.NewOutputType(model.NewObjectType(map[string]model.Type{
"bar": model.StringType,
})),
"baz": model.NewOutputType(model.NewListType(model.StringType)),
})
scope := model.NewRootScope(syntax.None)
scope.Define("key", &model.Variable{
Name: "key",
VariableType: model.StringType,
})
scope.Define("resource", &model.Variable{
Name: "resource",
VariableType: resourceType,
})
scope.Define("resources", &model.Variable{
Name: "resources",
VariableType: model.NewListType(resourceType),
})
scope.Define("resourcesPromise", &model.Variable{
Name: "resourcesPromise",
VariableType: model.NewPromiseType(model.NewListType(resourceType)),
})
scope.Define("resourcesOutput", &model.Variable{
Name: "resourcesOutput",
VariableType: model.NewOutputType(model.NewListType(resourceType)),
})
scope.DefineFunction("element", pulumiBuiltins["element"])
scope.DefineFunction("toJSON", pulumiBuiltins["toJSON"])
scope.DefineFunction("getPromise", model.NewFunction(model.StaticFunctionSignature{
Parameters: []model.Parameter{{
Name: "p",
Type: model.NewOptionalType(model.StringType),
}},
ReturnType: model.NewPromiseType(model.NewObjectType(map[string]model.Type{
"property": model.StringType,
"object": model.NewObjectType(map[string]model.Type{
"foo": model.StringType,
}),
})),
}))
for _, c := range cases {
t.Run(c.input, func(t *testing.T) {
expr, diags := model.BindExpressionText(c.input, scope, hcl.Pos{})
assert.Len(t, diags, 0)
expr, diags = RewriteApplies(expr, nameInfo(0), !c.skipPromises)
assert.Len(t, diags, 0)
assert.Equal(t, c.output, fmt.Sprintf("%v", expr))
})
}
}