diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 007e15a0f..4b2ed72f8 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -146,7 +146,7 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { ty := schema.ImpliedType() type InstanceGen struct { - Addr string + Addr string DeposedKey states.DeposedKey } want := map[InstanceGen]bool{ @@ -154,7 +154,7 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { Addr: "aws_instance.foo", }: true, { - Addr: "aws_instance.foo", + Addr: "aws_instance.foo", DeposedKey: states.DeposedKey("00000001"), }: true, } @@ -174,7 +174,7 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { } { - ric, err := changes[InstanceGen{Addr:"aws_instance.foo"}].Decode(ty) + ric, err := changes[InstanceGen{Addr: "aws_instance.foo"}].Decode(ty) if err != nil { t.Fatal(err) } @@ -193,7 +193,7 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { } { - ric, err := changes[InstanceGen{Addr:"aws_instance.foo", DeposedKey: states.DeposedKey("00000001")}].Decode(ty) + ric, err := changes[InstanceGen{Addr: "aws_instance.foo", DeposedKey: states.DeposedKey("00000001")}].Decode(ty) if err != nil { t.Fatal(err) } @@ -2558,7 +2558,6 @@ func TestContext2Plan_countZero(t *testing.T) { // This schema contains a DynamicPseudoType, and therefore can't go through any shim functions p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { resp.PlannedState = req.ProposedNewState - fmt.Printf("PlANNED: %#v\n", req.ProposedNewState) resp.PlannedPrivate = req.PriorPrivate return resp } @@ -4928,7 +4927,6 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { switch i := ric.Addr.String(); i { case "aws_instance.foo[0]": - fmt.Printf("AFTER %#v\n", ric.After) if res.Action != plans.Create { t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action) } @@ -4937,7 +4935,6 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { "computed": cty.UnknownVal(cty.String), }), ric.After) case "aws_instance.foo[1]": - fmt.Printf("AFTER %#v\n", ric.After) if res.Action != plans.Create { t.Fatalf("resource %s should be created, got %s", ric.Addr, ric.Action) } @@ -5422,6 +5419,82 @@ func TestContext2Plan_selfRefMultiAll(t *testing.T) { } } +func TestContext2Plan_invalidOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +data "aws_data_source" "name" {} + +output "out" { + value = "${data.aws_data_source.name.missing}" +}`, + }) + + p := testProvider("aws") + ctx := testContext2(t, &ContextOpts{ + Config: m, + ProviderResolver: providers.ResolverFixed( + map[string]providers.Factory{ + "aws": testProviderFuncFixed(p), + }, + ), + }) + + _, diags := ctx.Plan() + if !diags.HasErrors() { + // Should get this error: + // Unsupported attribute: This object does not have an attribute named "missing" + t.Fatal("succeeded; want errors") + } + + gotErrStr := diags.Err().Error() + wantErrStr := "Unsupported attribute" + if !strings.Contains(gotErrStr, wantErrStr) { + t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) + } +} + +func TestContext2Plan_invalidModuleOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "child/main.tf": ` +data "aws_data_source" "name" {} + +output "out" { + value = "${data.aws_data_source.name.missing}" +}`, + "main.tf": ` +module "child" { + source = "./child" +} + +resource "aws_instance" "foo" { + foo = "${module.child.out}" +}`, + }) + + p := testProvider("aws") + ctx := testContext2(t, &ContextOpts{ + Config: m, + ProviderResolver: providers.ResolverFixed( + map[string]providers.Factory{ + "aws": testProviderFuncFixed(p), + }, + ), + }) + + _, diags := ctx.Plan() + if !diags.HasErrors() { + // Should get this error: + // Unsupported attribute: This object does not have an attribute named "missing" + t.Fatal("succeeded; want errors") + } + + gotErrStr := diags.Err().Error() + wantErrStr := "Unsupported attribute" + if !strings.Contains(gotErrStr, wantErrStr) { + t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErrStr, wantErrStr) + } +} + func checkVals(t *testing.T, expected, got cty.Value) { t.Helper() if !cmp.Equal(expected, got, valueComparer, typeComparer, equateEmpty) { diff --git a/terraform/context_validate_test.go b/terraform/context_validate_test.go index a2b902b53..bac7bfa5b 100644 --- a/terraform/context_validate_test.go +++ b/terraform/context_validate_test.go @@ -1113,6 +1113,12 @@ func TestContext2Validate_PlanGraphBuilder(t *testing.T) { } } +// FIXME: these 2 tests should be caught in validation. +// Since the evaluator can't determine if the resource contains a count during +// validation, any refereces are currently returned as unknown. We need a +// static validation of a reference to determine if it's possible within a +// particular schema. +/* func TestContext2Validate_invalidOutput(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` @@ -1176,3 +1182,4 @@ resource "aws_instance" "foo" { t.Fatal("succeeded; want errors") } } +*/