testharness: "resource" context constructor

The result of calling this can be passed into the first argument of
"describe" to create a description with a resource as context. In child
blocks it can also be used to access the attributes of a resource selected
by a parent block.
proto-test-harness
Martin Atkins 9 years ago
parent adb109a8db
commit a4bbf16f83

@ -42,7 +42,7 @@ type resourceContextSetter struct {
DefRange tfdiags.SourceRange
}
func (s resourceContextSetter) Context(parent *Context, subject *Subject) (*Context, tfdiags.Diagnostics) {
func (s *resourceContextSetter) Context(parent *Context, subject *Subject) (*Context, tfdiags.Diagnostics) {
// TODO: Set the resource object too
return parent.WithNameSuffix(s.Addr.String()), nil
}

@ -140,10 +140,12 @@ func loadSpec(r io.Reader, filename string, L *lua.LState) (*Spec, tfdiags.Diagn
Diags: builderDiags,
}
testersB := testersBuilder{
Diags: builderDiags,
Context: RootContext,
Diags: builderDiags,
}
topEnv.RawSet(lua.LString("scenario"), L.NewFunction(scenariosB.luaScenarioFunc))
topEnv.RawSet(lua.LString("describe"), L.NewFunction(testersB.luaDescribeFunc))
topEnv.RawSet(lua.LString("resource"), testersB.luaResourceObj(L))
L.Push(fn)
err = L.PCall(0, 0, nil)

@ -61,7 +61,7 @@ func TestLoadSpecDir(t *testing.T) {
}
{
if got, want := len(spec.testers), 1; got != want {
if got, want := len(spec.testers), 2; got != want {
t.Fatalf("wrong number of testers %d; want %d", got, want)
}
@ -77,5 +77,18 @@ func TestLoadSpecDir(t *testing.T) {
} else {
t.Errorf("spec.testers[0] is %T; want *describe", spec.testers[0])
}
resourceTester := spec.testers[1]
if resourceTester, isResource := resourceTester.(*describe); isResource {
if resourceCtxSet, isResource := resourceTester.Described.(*resourceContextSetter); isResource {
if got, want := resourceCtxSet.Addr.String(), "test.foo"; got != want {
t.Errorf("spec.testers[1] has wrong resource address %q; want %q", got, want)
}
} else {
t.Errorf("spec.testers[1].Described is %T; want resourceContextSetter", resourceTester.Described)
}
} else {
t.Errorf("spec.testers[1] is %T; want *describe", spec.testers[0])
}
}
}

@ -6,3 +6,7 @@ end)
describe("foo", function ()
end)
describe(resource("test.foo"), function ()
end)

@ -1,14 +1,19 @@
package testharness
import (
"fmt"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/tfdiags"
lua "github.com/yuin/gopher-lua"
"github.com/zclconf/gopherlua-cty/luacty"
)
// testersBuilder provides functions for constructing a sequence of testers
// from within a Lua test spec.
type testersBuilder struct {
Context *Context
Testers Testers
Diags *Diagnostics
}
@ -71,3 +76,51 @@ func (b *testersBuilder) luaDescribeFunc(L *lua.LState) int {
return 0
}
func (b *testersBuilder) luaResourceObj(L *lua.LState) lua.LValue {
ret := L.NewUserData()
meta := L.NewTable()
conv := luacty.NewConverter(L)
if b.Context.HasResource() {
meta.RawSet(lua.LString("__index"), conv.WrapCtyValue(b.Context.Resource()))
meta.RawSet(lua.LString("__call"), L.NewFunction(func(L *lua.LState) int {
L.Error(lua.LString("a resource is already being described by a parent block"), 2)
return 0
}))
} else {
meta.RawSet(lua.LString("__index"), L.NewFunction(func(L *lua.LState) int {
L.Error(lua.LString("no resource is being described by the current block"), 2)
return 0
}))
meta.RawSet(lua.LString("__call"), L.NewFunction(func(L *lua.LState) int {
addrS := L.CheckString(2)
addr, err := terraform.ParseResourceAddress(addrS)
if err != nil {
L.Error(lua.LString(fmt.Sprintf("invalid resource address: %s", err)), 2)
return 0
}
if addr.Name == "" {
L.Error(lua.LString("invalid resource address: must refer to specific resource"), 2)
return 0
}
ctxSet := &resourceContextSetter{
Addr: addr,
}
if rng := callingRange(L, 1); rng != nil {
ctxSet.DefRange = tfdiags.SourceRangeFromHCL(*rng)
}
ret := L.NewUserData()
ret.Value = ctxSet
L.Push(ret)
return 1
}))
}
ret.Metatable = meta
return ret
}

Loading…
Cancel
Save