core: Update TestContext2Apply_multiVarComprehensive for new assumptions

This comprehensive test was covering a few different behaviors that are
intentionally different for v0.12:

- Applying the splat operator to a list of resource instances that haven't
  been created yet produces a list of unknown values rather than a single
  unknown list as before. This is important because it allows that list
  to be passed into length().

- Wrapping a splat expression in another round of brackets now produces
  a list of lists, whereas before we had a special case (for compatibility
  with prior to v0.10) that would flatten this away in the schema layer.
This commit is contained in:
Martin Atkins 2018-05-30 16:07:52 -07:00
parent cdce0d7e27
commit 3e64311dc2
3 changed files with 92 additions and 65 deletions

View File

@ -14,6 +14,7 @@ import (
"time"
"github.com/davecgh/go-spew/spew"
"github.com/go-test/deep"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/addrs"
@ -3514,8 +3515,8 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
"source_names": {Type: cty.List(cty.String), Optional: true},
"source_ids_from_func": {Type: cty.List(cty.String), Optional: true},
"source_names_from_func": {Type: cty.List(cty.String), Optional: true},
"source_ids_wrapped": {Type: cty.List(cty.String), Optional: true},
"source_names_wrapped": {Type: cty.List(cty.String), Optional: true},
"source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
"source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
"id": {Type: cty.String, Computed: true},
"name": {Type: cty.String, Computed: true},
@ -3546,32 +3547,45 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
checkConfig := func(name string, want map[string]interface{}) {
got := configs[name].Config
if !reflect.DeepEqual(got, want) {
t.Errorf(
"wrong config for %s\ngot: %s\nwant: %s",
name, spew.Sdump(got), spew.Sdump(want),
)
t.Run("config for "+name, func(t *testing.T) {
for _, problem := range deep.Equal(got, want) {
t.Errorf(problem)
}
})
}
checkConfig("test_thing.multi_count_var.0", map[string]interface{}{
"id": unknownValue(),
"name": unknownValue(),
"source_id": unknownValue(),
"source_name": "test_thing.source.0",
})
checkConfig("test_thing.multi_count_var.2", map[string]interface{}{
"id": unknownValue(),
"name": unknownValue(),
"source_id": unknownValue(),
"source_name": "test_thing.source.2",
})
checkConfig("test_thing.multi_count_derived.0", map[string]interface{}{
"id": unknownValue(),
"name": unknownValue(),
"source_id": unknownValue(),
"source_name": "test_thing.source.0",
})
checkConfig("test_thing.multi_count_derived.2", map[string]interface{}{
"id": unknownValue(),
"name": unknownValue(),
"source_id": unknownValue(),
"source_name": "test_thing.source.2",
})
checkConfig("test_thing.whole_splat", map[string]interface{}{
"source_ids": unknownValue(),
"id": unknownValue(),
"name": unknownValue(),
"source_ids": []interface{}{
unknownValue(),
unknownValue(),
unknownValue(),
},
"source_names": []interface{}{
"test_thing.source.0",
"test_thing.source.1",
@ -3584,47 +3598,60 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
"test_thing.source.2",
},
// This one ends up being a list with a single unknown value at this
// layer, but is fixed up inside helper/schema. There is a test for
// this inside the "test" provider, since core tests can't exercise
// helper/schema functionality.
"source_ids_wrapped": []interface{}{unknownValue()},
"source_ids_wrapped": []interface{}{
[]interface{}{
unknownValue(),
unknownValue(),
unknownValue(),
},
},
"source_names_wrapped": []interface{}{
[]interface{}{
"test_thing.source.0",
"test_thing.source.1",
"test_thing.source.2",
},
},
"first_source_id": unknownValue(),
"first_source_name": "test_thing.source.0",
})
checkConfig("module.child.test_thing.whole_splat", map[string]interface{}{
"source_ids": unknownValue(),
"id": unknownValue(),
"name": unknownValue(),
"source_ids": []interface{}{
unknownValue(),
unknownValue(),
unknownValue(),
},
"source_names": []interface{}{
"test_thing.source.0",
"test_thing.source.1",
"test_thing.source.2",
},
// This one ends up being a list with a single unknown value at this
// layer, but is fixed up inside helper/schema. There is a test for
// this inside the "test" provider, since core tests can't exercise
// helper/schema functionality.
"source_ids_wrapped": []interface{}{unknownValue()},
"source_ids_wrapped": []interface{}{
[]interface{}{
unknownValue(),
unknownValue(),
unknownValue(),
},
},
"source_names_wrapped": []interface{}{
[]interface{}{
"test_thing.source.0",
"test_thing.source.1",
"test_thing.source.2",
},
},
})
t.Run("apply", func(t *testing.T) {
state, diags := ctx.Apply()
if diags.HasErrors() {
t.Fatalf("error during apply: %s", diags.Err())
}
{
want := map[string]interface{}{
"source_ids": []interface{}{"foo", "foo", "foo"},
"source_names": []interface{}{
@ -3643,7 +3670,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
spew.Sdump(got), spew.Sdump(want),
)
}
}
})
}
// Test that multi-var (splat) access is ordered by count, not by

View File

@ -10,16 +10,16 @@ variable "source_names" {
}
resource "test_thing" "multi_count_var" {
count = "${var.num}"
count = var.num
# Can pluck a single item out of a multi-var
source_id = "${var.source_ids[count.index]}"
source_id = var.source_ids[count.index]
}
resource "test_thing" "whole_splat" {
# Can "splat" the ids directly into an attribute of type list.
source_ids = "${var.source_ids}"
source_names = "${var.source_names}"
source_ids = var.source_ids
source_names = var.source_names
source_ids_wrapped = ["${var.source_ids}"]
source_names_wrapped = ["${var.source_names}"]
}

View File

@ -2,48 +2,48 @@ variable "num" {
}
resource "test_thing" "source" {
count = "${var.num}"
count = var.num
# The diffFunc in the test exports "name" here too, which we can use
# to test values that are known during plan.
}
resource "test_thing" "multi_count_var" {
count = "${var.num}"
count = var.num
# Can pluck a single item out of a multi-var
source_id = "${test_thing.source.*.id[count.index]}"
source_name = "${test_thing.source.*.name[count.index]}"
source_id = test_thing.source.*.id[count.index]
source_name = test_thing.source.*.name[count.index]
}
resource "test_thing" "multi_count_derived" {
# Can use the source to get the count
count = "${length(test_thing.source)}"
count = length(test_thing.source)
source_id = "${test_thing.source.*.id[count.index]}"
source_name = "${test_thing.source.*.name[count.index]}"
source_id = test_thing.source.*.id[count.index]
source_name = test_thing.source.*.name[count.index]
}
resource "test_thing" "whole_splat" {
# Can "splat" the ids directly into an attribute of type list.
source_ids = "${test_thing.source.*.id}"
source_names = "${test_thing.source.*.name}"
source_ids = test_thing.source.*.id
source_names = test_thing.source.*.name
# Accessing through a function should work.
source_ids_from_func = "${split(" ", join(" ", test_thing.source.*.id))}"
source_names_from_func = "${split(" ", join(" ", test_thing.source.*.name))}"
source_ids_from_func = split(" ", join(" ", test_thing.source.*.id))
source_names_from_func = split(" ", join(" ", test_thing.source.*.name))
# A common pattern of selecting with a default.
first_source_id = "${element(concat(test_thing.source.*.id, list("default")), 0)}"
first_source_name = "${element(concat(test_thing.source.*.name, list("default")), 0)}"
first_source_id = element(concat(test_thing.source.*.id, ["default"]), 0)
first_source_name = element(concat(test_thing.source.*.name, ["default"]), 0)
# Legacy form: Prior to Terraform having comprehensive list support,
# splats were treated as a special case and required to be presented
# in a wrapping list. This is no longer the suggested form, but we
# need it to keep working for compatibility.
#
# This should result in exactly the same result as the above, even
# though it looks like it would result in a list of lists.
# Prior to v0.12 we were handling lists containing list interpolations as
# a special case, flattening the result, for compatibility with behavior
# prior to v0.10. This deprecated handling is now removed, and so these
# each produce a list of lists. We're still using the interpolation syntax
# here, rather than the splat expression directly, to properly mimic how
# this would've looked prior to v0.12 to be explicit about what the new
# behavior is for this old syntax.
source_ids_wrapped = ["${test_thing.source.*.id}"]
source_names_wrapped = ["${test_thing.source.*.name}"]
@ -52,15 +52,15 @@ resource "test_thing" "whole_splat" {
module "child" {
source = "./child"
num = "${var.num}"
source_ids = "${test_thing.source.*.id}"
source_names = "${test_thing.source.*.name}"
num = var.num
source_ids = test_thing.source.*.id
source_names = test_thing.source.*.name
}
output "source_ids" {
value = "${test_thing.source.*.id}"
value = test_thing.source.*.id
}
output "source_names" {
value = "${test_thing.source.*.name}"
value = test_thing.source.*.name
}