opentofu/helper/schema/resource_timeout_test.go
Sean Chittenden d749420a25
Fix drift caused from gofmt when running make dev and go 1.11.
A fresh checkout of `origin/master` does not build atm using the `dev`
target because `master` has not been formatted using `gofmt` from Go
1.11 (tis has been the case for a while if you've been running devel).

None of the drift in question is especially new but now that Go 1.11
has been released and gofmt's formatting guidelines have been updated,
it would be *really* nice if the code in `master` reflected the current
tooling in order to avoid having to fight this drift locally.

* 8mo: https://github.com/hashicorp/terraform/blame/master/backend/remote-state/s3/backend_test.go#L260-L261
* 6mo: https://github.com/hashicorp/terraform/blame/master/builtin/provisioners/chef/linux_provisioner_test.go#L124
* 1yr: 7cfeffe36b/command/init.go (L75-L76)
* 12d: 7cfeffe36b/command/meta_backend_test.go (L1437)
* 2yr: 7cfeffe36b/helper/schema/resource_timeout_test.go (L26)
* 4yr: 7cfeffe36b/helper/schema/schema_test.go (L2059)
* 1yr: 7cfeffe36b/plugin/discovery/get_test.go (L151)
2018-09-09 10:18:08 -07:00

353 lines
9.4 KiB
Go

package schema
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/terraform"
)
func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) {
cases := []struct {
Name string
// what the resource has defined in source
ResourceDefaultTimeout *ResourceTimeout
// configuration provider by user in tf file
Config []map[string]interface{}
// what we expect the parsed ResourceTimeout to be
Expected *ResourceTimeout
// Should we have an error (key not defined in source)
ShouldErr bool
}{
{
Name: "Source does not define 'delete' key",
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
Config: expectedConfigForValues(2, 0, 0, 1, 0),
Expected: timeoutForValues(10, 0, 5, 0, 0),
ShouldErr: true,
},
{
Name: "Config overrides create",
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0),
Config: expectedConfigForValues(2, 0, 7, 0, 0),
Expected: timeoutForValues(2, 0, 7, 0, 0),
ShouldErr: false,
},
{
Name: "Config overrides create, default provided. Should still have zero values",
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
Config: expectedConfigForValues(2, 0, 7, 0, 0),
Expected: timeoutForValues(2, 0, 7, 0, 3),
ShouldErr: false,
},
{
Name: "Use something besides 'minutes'",
ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3),
Config: []map[string]interface{}{
map[string]interface{}{
"create": "2h",
}},
Expected: timeoutForValues(120, 0, 5, 0, 3),
ShouldErr: false,
},
}
for i, c := range cases {
t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) {
r := &Resource{
Timeouts: c.ResourceDefaultTimeout,
}
raw, err := config.NewRawConfig(
map[string]interface{}{
"foo": "bar",
TimeoutsConfigKey: c.Config,
})
if err != nil {
t.Fatalf("err: %s", err)
}
conf := terraform.NewResourceConfig(raw)
timeout := &ResourceTimeout{}
decodeErr := timeout.ConfigDecode(r, conf)
if c.ShouldErr {
if decodeErr == nil {
t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr)
}
// should error, err was not nil, continue
return
} else {
if decodeErr != nil {
// should not error, error was not nil, fatal
t.Fatalf("decodeError was not nil: %s", decodeErr)
}
}
if !reflect.DeepEqual(c.Expected, timeout) {
t.Fatalf("ConfigDecode match error case (%d), expected:\n%#v\ngot:\n%#v", i, c.Expected, timeout)
}
})
}
}
func TestResourceTimeout_ConfigDecode(t *testing.T) {
r := &Resource{
Timeouts: &ResourceTimeout{
Create: DefaultTimeout(10 * time.Minute),
Update: DefaultTimeout(5 * time.Minute),
},
}
raw, err := config.NewRawConfig(
map[string]interface{}{
"foo": "bar",
TimeoutsConfigKey: []map[string]interface{}{
map[string]interface{}{
"create": "2m",
},
map[string]interface{}{
"update": "1m",
},
},
})
if err != nil {
t.Fatalf("err: %s", err)
}
c := terraform.NewResourceConfig(raw)
timeout := &ResourceTimeout{}
err = timeout.ConfigDecode(r, c)
if err != nil {
t.Fatalf("Expected good timeout returned:, %s", err)
}
expected := &ResourceTimeout{
Create: DefaultTimeout(2 * time.Minute),
Update: DefaultTimeout(1 * time.Minute),
}
if !reflect.DeepEqual(timeout, expected) {
t.Fatalf("bad timeout decode, expected (%#v), got (%#v)", expected, timeout)
}
}
func TestResourceTimeout_DiffEncode_basic(t *testing.T) {
cases := []struct {
Timeout *ResourceTimeout
Expected map[string]interface{}
// Not immediately clear when an error would hit
ShouldErr bool
}{
// Two fields
{
Timeout: timeoutForValues(10, 0, 5, 0, 0),
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)},
ShouldErr: false,
},
// Two fields, one is Default
{
Timeout: timeoutForValues(10, 0, 0, 0, 7),
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)},
ShouldErr: false,
},
// All fields
{
Timeout: timeoutForValues(10, 3, 4, 1, 7),
Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)},
ShouldErr: false,
},
// No fields
{
Timeout: &ResourceTimeout{},
Expected: nil,
ShouldErr: false,
},
}
for _, c := range cases {
state := &terraform.InstanceDiff{}
err := c.Timeout.DiffEncode(state)
if err != nil && !c.ShouldErr {
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
}
// should maybe just compare [TimeoutKey] but for now we're assuming only
// that in Meta
if !reflect.DeepEqual(state.Meta, c.Expected) {
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
}
}
// same test cases but for InstanceState
for _, c := range cases {
state := &terraform.InstanceState{}
err := c.Timeout.StateEncode(state)
if err != nil && !c.ShouldErr {
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta)
}
// should maybe just compare [TimeoutKey] but for now we're assuming only
// that in Meta
if !reflect.DeepEqual(state.Meta, c.Expected) {
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta)
}
}
}
func TestResourceTimeout_MetaDecode_basic(t *testing.T) {
cases := []struct {
State *terraform.InstanceDiff
Expected *ResourceTimeout
// Not immediately clear when an error would hit
ShouldErr bool
}{
// Two fields
{
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}},
Expected: timeoutForValues(10, 0, 5, 0, 0),
ShouldErr: false,
},
// Two fields, one is Default
{
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}},
Expected: timeoutForValues(10, 7, 7, 7, 7),
ShouldErr: false,
},
// All fields
{
State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}},
Expected: timeoutForValues(10, 3, 4, 1, 7),
ShouldErr: false,
},
// No fields
{
State: &terraform.InstanceDiff{},
Expected: &ResourceTimeout{},
ShouldErr: false,
},
}
for _, c := range cases {
rt := &ResourceTimeout{}
err := rt.DiffDecode(c.State)
if err != nil && !c.ShouldErr {
t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt)
}
// should maybe just compare [TimeoutKey] but for now we're assuming only
// that in Meta
if !reflect.DeepEqual(rt, c.Expected) {
t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt)
}
}
}
func timeoutForValues(create, read, update, del, def int) *ResourceTimeout {
rt := ResourceTimeout{}
if create != 0 {
rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
}
if read != 0 {
rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
}
if update != 0 {
rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
}
if del != 0 {
rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
}
if def != 0 {
rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
}
return &rt
}
// Generates a ResourceTimeout struct that should reflect the
// d.Timeout("key") results
func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout {
rt := ResourceTimeout{}
defaultValues := []*int{&create, &read, &update, &del, &def}
for _, v := range defaultValues {
if *v == 0 {
*v = 20
}
}
if create != 0 {
rt.Create = DefaultTimeout(time.Duration(create) * time.Minute)
}
if read != 0 {
rt.Read = DefaultTimeout(time.Duration(read) * time.Minute)
}
if update != 0 {
rt.Update = DefaultTimeout(time.Duration(update) * time.Minute)
}
if del != 0 {
rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute)
}
if def != 0 {
rt.Default = DefaultTimeout(time.Duration(def) * time.Minute)
}
return &rt
}
func expectedForValues(create, read, update, del, def int) map[string]interface{} {
ex := make(map[string]interface{})
if create != 0 {
ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds()
}
if read != 0 {
ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds()
}
if update != 0 {
ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds()
}
if del != 0 {
ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds()
}
if def != 0 {
defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds()
ex["default"] = defNano
for _, k := range timeoutKeys() {
if _, ok := ex[k]; !ok {
ex[k] = defNano
}
}
}
return ex
}
func expectedConfigForValues(create, read, update, delete, def int) []map[string]interface{} {
ex := make([]map[string]interface{}, 0)
if create != 0 {
ex = append(ex, map[string]interface{}{"create": fmt.Sprintf("%dm", create)})
}
if read != 0 {
ex = append(ex, map[string]interface{}{"read": fmt.Sprintf("%dm", read)})
}
if update != 0 {
ex = append(ex, map[string]interface{}{"update": fmt.Sprintf("%dm", update)})
}
if delete != 0 {
ex = append(ex, map[string]interface{}{"delete": fmt.Sprintf("%dm", delete)})
}
if def != 0 {
ex = append(ex, map[string]interface{}{"default": fmt.Sprintf("%dm", def)})
}
return ex
}