mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
5f9b189fcf
Errors were being ignore with the intention that they would be caught later in validation, but it turns out we nee dto catch those earlier. The legacy schemas also allowed providers to set and empty string for a bool value, which we need to handle here, since it's not being handled from user input like a normal config value.
717 lines
17 KiB
Go
717 lines
17 KiB
Go
package plugin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/hashicorp/terraform/config/hcl2shim"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
proto "github.com/hashicorp/terraform/internal/tfplugin5"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/msgpack"
|
|
)
|
|
|
|
// The GRPCProviderServer will directly implement the go protobuf server
|
|
var _ proto.ProviderServer = (*GRPCProviderServer)(nil)
|
|
|
|
var (
|
|
typeComparer = cmp.Comparer(cty.Type.Equals)
|
|
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
|
equateEmpty = cmpopts.EquateEmpty()
|
|
)
|
|
|
|
func TestUpgradeState_jsonState(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 2,
|
|
Schema: map[string]*schema.Schema{
|
|
"two": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
r.StateUpgraders = []schema.StateUpgrader{
|
|
{
|
|
Version: 0,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"zero": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["zero"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("zero not found in %#v", m)
|
|
}
|
|
m["one"] = float64(1)
|
|
delete(m, "zero")
|
|
return m, nil
|
|
},
|
|
},
|
|
{
|
|
Version: 1,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"one": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["one"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("one not found in %#v", m)
|
|
}
|
|
m["two"] = float64(2)
|
|
delete(m, "one")
|
|
return m, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
req := &proto.UpgradeResourceState_Request{
|
|
TypeName: "test",
|
|
Version: 0,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","zero":0}`),
|
|
},
|
|
}
|
|
|
|
resp, err := server.UpgradeResourceState(nil, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(resp.Diagnostics) > 0 {
|
|
for _, d := range resp.Diagnostics {
|
|
t.Errorf("%#v", d)
|
|
}
|
|
t.Fatal("error")
|
|
}
|
|
|
|
val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.StringVal("bar"),
|
|
"two": cty.NumberIntVal(2),
|
|
})
|
|
|
|
if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
|
|
t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
|
|
}
|
|
}
|
|
|
|
func TestUpgradeState_flatmapState(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 4,
|
|
Schema: map[string]*schema.Schema{
|
|
"four": {
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
// this MigrateState will take the state to version 2
|
|
MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) {
|
|
switch v {
|
|
case 0:
|
|
_, ok := is.Attributes["zero"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("zero not found in %#v", is.Attributes)
|
|
}
|
|
is.Attributes["one"] = "1"
|
|
delete(is.Attributes, "zero")
|
|
fallthrough
|
|
case 1:
|
|
_, ok := is.Attributes["one"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("one not found in %#v", is.Attributes)
|
|
}
|
|
is.Attributes["two"] = "2"
|
|
delete(is.Attributes, "one")
|
|
default:
|
|
return nil, fmt.Errorf("invalid schema version %d", v)
|
|
}
|
|
return is, nil
|
|
},
|
|
}
|
|
|
|
r.StateUpgraders = []schema.StateUpgrader{
|
|
{
|
|
Version: 2,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"two": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["two"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("two not found in %#v", m)
|
|
}
|
|
m["three"] = float64(3)
|
|
delete(m, "two")
|
|
return m, nil
|
|
},
|
|
},
|
|
{
|
|
Version: 3,
|
|
Type: cty.Object(map[string]cty.Type{
|
|
"id": cty.String,
|
|
"three": cty.Number,
|
|
}),
|
|
Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
|
|
_, ok := m["three"].(float64)
|
|
if !ok {
|
|
return nil, fmt.Errorf("three not found in %#v", m)
|
|
}
|
|
m["four"] = float64(4)
|
|
delete(m, "three")
|
|
return m, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
testReqs := []*proto.UpgradeResourceState_Request{
|
|
{
|
|
TypeName: "test",
|
|
Version: 0,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"zero": "0",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 1,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"one": "1",
|
|
},
|
|
},
|
|
},
|
|
// two and up could be stored in flatmap or json states
|
|
{
|
|
TypeName: "test",
|
|
Version: 2,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"two": "2",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 2,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","two":2}`),
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 3,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"three": "3",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 3,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","three":3}`),
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 4,
|
|
RawState: &proto.RawState{
|
|
Flatmap: map[string]string{
|
|
"id": "bar",
|
|
"four": "4",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
TypeName: "test",
|
|
Version: 4,
|
|
RawState: &proto.RawState{
|
|
Json: []byte(`{"id":"bar","four":4}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, req := range testReqs {
|
|
t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) {
|
|
resp, err := server.UpgradeResourceState(nil, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(resp.Diagnostics) > 0 {
|
|
for _, d := range resp.Diagnostics {
|
|
t.Errorf("%#v", d)
|
|
}
|
|
t.Fatal("error")
|
|
}
|
|
|
|
val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expected := cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.StringVal("bar"),
|
|
"four": cty.NumberIntVal(4),
|
|
})
|
|
|
|
if !cmp.Equal(expected, val, valueComparer, equateEmpty) {
|
|
t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPlanResourceChange(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 4,
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
schema := r.CoreConfigSchema()
|
|
priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// A propsed state with only the ID unknown will produce a nil diff, and
|
|
// should return the propsed state value.
|
|
proposedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.UnknownVal(cty.String),
|
|
}))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testReq := &proto.PlanResourceChange_Request{
|
|
TypeName: "test",
|
|
PriorState: &proto.DynamicValue{
|
|
Msgpack: priorState,
|
|
},
|
|
ProposedNewState: &proto.DynamicValue{
|
|
Msgpack: proposedState,
|
|
},
|
|
}
|
|
|
|
resp, err := server.PlanResourceChange(context.Background(), testReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.Msgpack, schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) {
|
|
t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer))
|
|
}
|
|
}
|
|
|
|
func TestApplyResourceChange(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 4,
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
Create: func(rd *schema.ResourceData, _ interface{}) error {
|
|
rd.SetId("bar")
|
|
return nil
|
|
},
|
|
}
|
|
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
ResourcesMap: map[string]*schema.Resource{
|
|
"test": r,
|
|
},
|
|
},
|
|
}
|
|
|
|
schema := r.CoreConfigSchema()
|
|
priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// A proposed state with only the ID unknown will produce a nil diff, and
|
|
// should return the proposed state value.
|
|
plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
"id": cty.UnknownVal(cty.String),
|
|
}))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testReq := &proto.ApplyResourceChange_Request{
|
|
TypeName: "test",
|
|
PriorState: &proto.DynamicValue{
|
|
Msgpack: priorState,
|
|
},
|
|
PlannedState: &proto.DynamicValue{
|
|
Msgpack: plannedState,
|
|
},
|
|
}
|
|
|
|
resp, err := server.ApplyResourceChange(context.Background(), testReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
newStateVal, err := msgpack.Unmarshal(resp.NewState.Msgpack, schema.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
id := newStateVal.GetAttr("id").AsString()
|
|
if id != "bar" {
|
|
t.Fatalf("incorrect final state: %#v\n", newStateVal)
|
|
}
|
|
}
|
|
|
|
func TestPrepareProviderConfig(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
Name string
|
|
Schema map[string]*schema.Schema
|
|
ConfigVal cty.Value
|
|
ExpectError string
|
|
ExpectConfig cty.Value
|
|
}{
|
|
{
|
|
Name: "test prepare",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("bar"),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("bar"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test default",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Default: "default",
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("default"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test defaultfunc",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
DefaultFunc: func() (interface{}, error) {
|
|
return "defaultfunc", nil
|
|
},
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("defaultfunc"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test default required",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
DefaultFunc: func() (interface{}, error) {
|
|
return "defaultfunc", nil
|
|
},
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("defaultfunc"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test incorrect type",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NumberIntVal(3),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("3"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test incorrect default type",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Default: true,
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.StringVal("true"),
|
|
}),
|
|
},
|
|
{
|
|
Name: "test incorrect default bool type",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
Default: "",
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.Bool),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.False,
|
|
}),
|
|
},
|
|
{
|
|
Name: "test deprecated default",
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Default: "do not use",
|
|
Removed: "don't use this",
|
|
},
|
|
},
|
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
|
"foo": cty.NullVal(cty.String),
|
|
}),
|
|
},
|
|
} {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
server := &GRPCProviderServer{
|
|
provider: &schema.Provider{
|
|
Schema: tc.Schema,
|
|
},
|
|
}
|
|
|
|
block := schema.InternalMap(tc.Schema).CoreConfigSchema()
|
|
|
|
rawConfig, err := msgpack.Marshal(tc.ConfigVal, block.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
testReq := &proto.PrepareProviderConfig_Request{
|
|
Config: &proto.DynamicValue{
|
|
Msgpack: rawConfig,
|
|
},
|
|
}
|
|
|
|
resp, err := server.PrepareProviderConfig(nil, testReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if tc.ExpectError != "" && len(resp.Diagnostics) > 0 {
|
|
for _, d := range resp.Diagnostics {
|
|
if !strings.Contains(d.Summary, tc.ExpectError) {
|
|
t.Fatalf("Unexpected error: %s/%s", d.Summary, d.Detail)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// we should have no errors past this point
|
|
for _, d := range resp.Diagnostics {
|
|
if d.Severity == proto.Diagnostic_ERROR {
|
|
t.Fatal(resp.Diagnostics)
|
|
}
|
|
}
|
|
|
|
val, err := msgpack.Unmarshal(resp.PreparedConfig.Msgpack, block.ImpliedType())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if tc.ExpectConfig.GoString() != val.GoString() {
|
|
t.Fatalf("\nexpected: %#v\ngot: %#v", tc.ExpectConfig, val)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetSchemaTimeouts(t *testing.T) {
|
|
r := &schema.Resource{
|
|
SchemaVersion: 4,
|
|
Timeouts: &schema.ResourceTimeout{
|
|
Create: schema.DefaultTimeout(time.Second),
|
|
Read: schema.DefaultTimeout(2 * time.Second),
|
|
Update: schema.DefaultTimeout(3 * time.Second),
|
|
Default: schema.DefaultTimeout(10 * time.Second),
|
|
},
|
|
Schema: map[string]*schema.Schema{
|
|
"foo": {
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
// verify that the timeouts appear in the schema as defined
|
|
block := r.CoreConfigSchema()
|
|
timeoutsBlock := block.BlockTypes["timeouts"]
|
|
if timeoutsBlock == nil {
|
|
t.Fatal("missing timeouts in schema")
|
|
}
|
|
|
|
if timeoutsBlock.Attributes["create"] == nil {
|
|
t.Fatal("missing create timeout in schema")
|
|
}
|
|
if timeoutsBlock.Attributes["read"] == nil {
|
|
t.Fatal("missing read timeout in schema")
|
|
}
|
|
if timeoutsBlock.Attributes["update"] == nil {
|
|
t.Fatal("missing update timeout in schema")
|
|
}
|
|
if d := timeoutsBlock.Attributes["delete"]; d != nil {
|
|
t.Fatalf("unexpected delete timeout in schema: %#v", d)
|
|
}
|
|
if timeoutsBlock.Attributes["default"] == nil {
|
|
t.Fatal("missing default timeout in schema")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeFlatmapContainers(t *testing.T) {
|
|
for i, tc := range []struct {
|
|
prior map[string]string
|
|
attrs map[string]string
|
|
expect map[string]string
|
|
}{
|
|
{
|
|
attrs: map[string]string{"id": "1", "multi.2.set.#": "1", "multi.1.set.#": "0", "single.#": "0"},
|
|
expect: map[string]string{"id": "1"},
|
|
},
|
|
{
|
|
attrs: map[string]string{"id": "1", "multi.2.set.#": "2", "multi.2.set.1.foo": "bar", "multi.1.set.#": "0", "single.#": "0"},
|
|
expect: map[string]string{"id": "1", "multi.2.set.#": "1", "multi.2.set.1.foo": "bar"},
|
|
},
|
|
{
|
|
attrs: map[string]string{"id": "78629a0f5f3f164f", "multi.#": "1"},
|
|
expect: map[string]string{"id": "78629a0f5f3f164f"},
|
|
},
|
|
{
|
|
attrs: map[string]string{"multi.529860700.set.#": "1", "multi.#": "1", "id": "78629a0f5f3f164f"},
|
|
expect: map[string]string{"id": "78629a0f5f3f164f"},
|
|
},
|
|
{
|
|
attrs: map[string]string{"set.2.required": "bar", "set.2.list.#": "1", "set.2.list.0": "x", "set.1.list.#": "0", "set.#": "2"},
|
|
expect: map[string]string{"set.2.list.#": "1", "set.2.list.0": "x", "set.2.required": "bar", "set.#": "1"},
|
|
},
|
|
{
|
|
attrs: map[string]string{"map.%": hcl2shim.UnknownVariableValue, "list.#": hcl2shim.UnknownVariableValue, "id": "1"},
|
|
expect: map[string]string{"id": "1", "map.%": hcl2shim.UnknownVariableValue, "list.#": hcl2shim.UnknownVariableValue},
|
|
},
|
|
{
|
|
prior: map[string]string{"map.%": "0"},
|
|
attrs: map[string]string{"map.%": "0", "list.#": "0", "id": "1"},
|
|
expect: map[string]string{"id": "1", "map.%": "0"},
|
|
},
|
|
{
|
|
prior: map[string]string{"map.%": hcl2shim.UnknownVariableValue, "list.#": "0"},
|
|
attrs: map[string]string{"map.%": "0", "list.#": "0", "id": "1"},
|
|
expect: map[string]string{"id": "1", "map.%": "0", "list.#": "0"},
|
|
},
|
|
} {
|
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
|
got := normalizeFlatmapContainers(tc.prior, tc.attrs)
|
|
if !reflect.DeepEqual(tc.expect, got) {
|
|
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", tc.expect, got)
|
|
}
|
|
})
|
|
}
|
|
}
|