repl: Make tests compile and execute without panics
There are still some errors left, because our expression evaluator now does more validation than before and so we'll need to (in a subsequent commit) actually use a fixture configuration for these tests so that the validations will allow the expressions to be validated.
This commit is contained in:
parent
85aa8769db
commit
5ff35c1a9a
|
@ -4,44 +4,44 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestSession_basicState(t *testing.T) {
|
||||
state := &terraform.State{
|
||||
Modules: []*terraform.ModuleState{
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_instance.foo": &terraform.ResourceState{
|
||||
Type: "test_instance",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
state := states.BuildState(func (s *states.SyncState) {
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_instance",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
|
||||
&terraform.ModuleState{
|
||||
Path: []string{"root", "module"},
|
||||
Resources: map[string]*terraform.ResourceState{
|
||||
"test_instance.foo": &terraform.ResourceState{
|
||||
Type: "test_instance",
|
||||
Primary: &terraform.InstanceState{
|
||||
ID: "bar",
|
||||
Attributes: map[string]string{
|
||||
"id": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
addrs.ProviderConfig{
|
||||
Type: "aws",
|
||||
}.Absolute(addrs.RootModuleInstance),
|
||||
)
|
||||
s.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_instance",
|
||||
Name: "foo",
|
||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("module", addrs.NoKey)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
addrs.ProviderConfig{
|
||||
Type: "aws",
|
||||
}.Absolute(addrs.RootModuleInstance),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
testSession(t, testSessionTest{
|
||||
|
@ -112,9 +112,8 @@ func TestSession_stateless(t *testing.T) {
|
|||
testSession(t, testSessionTest{
|
||||
Inputs: []testSessionInput{
|
||||
{
|
||||
Input: "exit",
|
||||
Error: true,
|
||||
ErrorContains: ErrSessionExit.Error(),
|
||||
Input: "exit",
|
||||
Exit: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -159,7 +158,7 @@ func TestSession_stateless(t *testing.T) {
|
|||
{
|
||||
Input: "test_instance.bar.id",
|
||||
Error: true,
|
||||
ErrorContains: "'test_instance.bar' not found",
|
||||
ErrorContains: `resource "test_instance" "bar" has not been declared`,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -167,34 +166,49 @@ func TestSession_stateless(t *testing.T) {
|
|||
}
|
||||
|
||||
func testSession(t *testing.T, test testSessionTest) {
|
||||
p := &terraform.MockProvider{}
|
||||
p.GetSchemaReturn = &terraform.ProviderSchema{}
|
||||
|
||||
// Build the TF context
|
||||
ctx, err := terraform.NewContext(&terraform.ContextOpts{
|
||||
ctx, diags := terraform.NewContext(&terraform.ContextOpts{
|
||||
State: test.State,
|
||||
Module: module.NewEmptyTree(),
|
||||
ProviderResolver: providers.ResolverFixed(map[string]providers.Factory{
|
||||
"aws": providers.FactoryFixed(p),
|
||||
}),
|
||||
Config: configs.NewEmptyConfig(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("failed to create context: %s", diags.Err())
|
||||
}
|
||||
|
||||
scope, diags := ctx.Eval(addrs.RootModuleInstance)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("failed to create scope: %s", diags.Err())
|
||||
}
|
||||
|
||||
// Build the session
|
||||
s := &Session{
|
||||
Interpolater: ctx.Interpolater(),
|
||||
Scope: scope,
|
||||
}
|
||||
|
||||
// Test the inputs. We purposely don't use subtests here because
|
||||
// the inputs don't recognize subtests, but a sequence of stateful
|
||||
// the inputs don't represent subtests, but a sequence of stateful
|
||||
// operations.
|
||||
for _, input := range test.Inputs {
|
||||
result, err := s.Handle(input.Input)
|
||||
if (err != nil) != input.Error {
|
||||
t.Fatalf("%q: err: %s", input.Input, err)
|
||||
result, exit, diags := s.Handle(input.Input)
|
||||
if exit != input.Exit {
|
||||
t.Fatalf("incorrect 'exit' result %t; want %t", exit, input.Exit)
|
||||
}
|
||||
if err != nil {
|
||||
if (diags.HasErrors()) != input.Error {
|
||||
t.Fatalf("%q: unexpected errors: %s", input.Input, diags.Err())
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
if input.ErrorContains != "" {
|
||||
if !strings.Contains(err.Error(), input.ErrorContains) {
|
||||
if !strings.Contains(diags.Err().Error(), input.ErrorContains) {
|
||||
t.Fatalf(
|
||||
"%q: err should contain: %q\n\n%s",
|
||||
input.Input, input.ErrorContains, err)
|
||||
"%q: diagnostics should contain: %q\n\n%s",
|
||||
input.Input, input.ErrorContains, diags.Err(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,8 +230,8 @@ func testSession(t *testing.T, test testSessionTest) {
|
|||
}
|
||||
|
||||
type testSessionTest struct {
|
||||
State *terraform.State // State to use
|
||||
Module string // Module name in test-fixtures to load
|
||||
State *states.State // State to use
|
||||
Module string // Module name in test-fixtures to load
|
||||
|
||||
// Inputs are the list of test inputs that are run in order.
|
||||
// Each input can test the output of each step.
|
||||
|
@ -230,5 +244,6 @@ type testSessionInput struct {
|
|||
Output string // Exact output string to check
|
||||
OutputContains string
|
||||
Error bool // Error is true if error is expected
|
||||
Exit bool // Exit is true if exiting is expected
|
||||
ErrorContains string
|
||||
}
|
||||
|
|
|
@ -329,9 +329,17 @@ func (d *evaluationStateData) GetModuleInstanceOutput(addr addrs.ModuleCallOutpu
|
|||
// name is declared at all.
|
||||
moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr)
|
||||
if moduleConfig == nil {
|
||||
// should never happen, since we can't be evaluating in a module
|
||||
// that wasn't mentioned in configuration.
|
||||
panic(fmt.Sprintf("output value read from %s, which has no configuration", moduleAddr))
|
||||
// this doesn't happen in normal circumstances due to our validation
|
||||
// pass, but it can turn up in some unusual situations, like in the
|
||||
// "terraform console" repl where arbitrary expressions can be
|
||||
// evaluated.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Reference to undeclared module`,
|
||||
Detail: fmt.Sprintf(`The configuration contains no %s.`, moduleAddr),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
config := moduleConfig.Module.Outputs[addr.Name]
|
||||
|
@ -348,7 +356,7 @@ func (d *evaluationStateData) GetModuleInstanceOutput(addr addrs.ModuleCallOutpu
|
|||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Reference to undeclared output value`,
|
||||
Detail: fmt.Sprintf(`An output value with the name %q has not been declared in %s.%s`, addr.Name, moduleAddr, suggestion),
|
||||
Detail: fmt.Sprintf(`An output value with the name %q has not been declared in %s.%s`, addr.Name, moduleDisplayAddr(moduleAddr), suggestion),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
|
@ -448,7 +456,7 @@ func (d *evaluationStateData) GetResourceInstance(addr addrs.ResourceInstance, r
|
|||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Reference to undeclared resource`,
|
||||
Detail: fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Resource.Type, addr.Resource.Name, moduleAddr),
|
||||
Detail: fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Resource.Type, addr.Resource.Name, moduleDisplayAddr(moduleAddr)),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
return cty.DynamicVal, diags
|
||||
|
@ -872,3 +880,17 @@ func nameSuggestion(given string, suggestions []string) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// moduleDisplayAddr returns a string describing the given module instance
|
||||
// address that is appropriate for returning to users in situations where the
|
||||
// root module is possible. Specifically, it returns "the root module" if the
|
||||
// root module instance is given, or a string representation of the module
|
||||
// address otherwise.
|
||||
func moduleDisplayAddr(addr addrs.ModuleInstance) string {
|
||||
switch {
|
||||
case addr.IsRoot():
|
||||
return "the root module"
|
||||
default:
|
||||
return addr.String()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue