opentofu/helper/customdiff/condition_test.go
Martin Atkins c647b22d97 helper/customdiff: Helper functions for CustomizeDiff
The CustomizeDiff functionality in helper/schema is powerful, but directly
writing single CustomizeDiff functions can obscure the intent when a
number of different, orthogonal diff-customization behaviors are required.

This new library provides some building blocks that aim to allow a more
declarative form of CustomizeDiff implementation, by composing a number of
smaller operations. For example:

     &schema.Resource{
         // ...
         CustomizeDiff: customdiff.All(
             customdiff.ValidateChange("size", func (old, new, meta interface{}) error {
                 // If we are increasing "size" then the new value must be
                 // a multiple of the old value.
                 if new.(int) <= old.(int) {
                     return nil
                 }
                 if (new.(int) % old.(int)) != 0 {
                     return fmt.Errorf("new size value must be an integer multiple of old value %d", old.(int))
                 }
                 return nil
             }),
             customdiff.ForceNewIfChange("size", func (old, new, meta interface{}) bool {
                 // "size" can only increase in-place, so we must create a new resource
                 // if it is decreased.
                 return new.(int) < old.(int)
             }),
             customdiff.ComputedIf("version_id", func (d *schema.ResourceDiff, meta interface{}) bool {
                 // Any change to "content" causes a new "version_id" to be allocated.
                 return d.HasChange("content")
             }),
         ),
     }

The goal is to allow the various separate operations to be quickly seen
and to ensure that each of them runs independently of the others. These
functions all create closures on the call parameters, so the result is
still just a normal CustomizeDiffFunc and so the helpers in this package
can be combined with hand-written functions as needed.

As we get more experience writing CustomizeDiff functions we may wish to
expand the repertoire of functions here in future; this initial set
attempts to cover some common cases we've seen so far. We may also
investigate some helper functions that are entirely declarative and so
don't take callback functions at all, but want to learn what the relevant
use-cases are before going in too deep here.
2017-12-18 10:38:20 -08:00

349 lines
7.0 KiB
Go

package customdiff
import (
"errors"
"testing"
"github.com/hashicorp/terraform/helper/schema"
)
func TestIf(t *testing.T) {
t.Run("true", func(t *testing.T) {
var condCalled, customCalled bool
var gotOld, gotNew string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
If(
func(d *schema.ResourceDiff, meta interface{}) bool {
condCalled = true
old, new := d.GetChange("foo")
gotOld = old.(string)
gotNew = new.(string)
return true
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err == nil {
t.Fatal("Diff succeeded; want error")
}
if got, want := err.Error(), "bad"; got != want {
t.Fatalf("wrong error message %q; want %q", got, want)
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotOld, "bar"; got != want {
t.Errorf("wrong old value %q; want %q", got, want)
}
if got, want := gotNew, "baz"; got != want {
t.Errorf("wrong new value %q; want %q", got, want)
}
}
if !customCalled {
t.Error("customize callback was not called")
}
})
t.Run("false", func(t *testing.T) {
var condCalled, customCalled bool
var gotOld, gotNew string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
If(
func(d *schema.ResourceDiff, meta interface{}) bool {
condCalled = true
old, new := d.GetChange("foo")
gotOld = old.(string)
gotNew = new.(string)
return false
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err != nil {
t.Fatalf("Diff error %q; want success", err.Error())
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotOld, "bar"; got != want {
t.Errorf("wrong old value %q; want %q", got, want)
}
if got, want := gotNew, "baz"; got != want {
t.Errorf("wrong new value %q; want %q", got, want)
}
}
if customCalled {
t.Error("customize callback was called (should not have been)")
}
})
}
func TestIfValueChange(t *testing.T) {
t.Run("true", func(t *testing.T) {
var condCalled, customCalled bool
var gotOld, gotNew string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
IfValueChange(
"foo",
func(old, new, meta interface{}) bool {
condCalled = true
gotOld = old.(string)
gotNew = new.(string)
return true
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err == nil {
t.Fatal("Diff succeeded; want error")
}
if got, want := err.Error(), "bad"; got != want {
t.Fatalf("wrong error message %q; want %q", got, want)
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotOld, "bar"; got != want {
t.Errorf("wrong old value %q; want %q", got, want)
}
if got, want := gotNew, "baz"; got != want {
t.Errorf("wrong new value %q; want %q", got, want)
}
}
if !customCalled {
t.Error("customize callback was not called")
}
})
t.Run("false", func(t *testing.T) {
var condCalled, customCalled bool
var gotOld, gotNew string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
IfValueChange(
"foo",
func(old, new, meta interface{}) bool {
condCalled = true
gotOld = old.(string)
gotNew = new.(string)
return false
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err != nil {
t.Fatalf("Diff error %q; want success", err.Error())
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotOld, "bar"; got != want {
t.Errorf("wrong old value %q; want %q", got, want)
}
if got, want := gotNew, "baz"; got != want {
t.Errorf("wrong new value %q; want %q", got, want)
}
}
if customCalled {
t.Error("customize callback was called (should not have been)")
}
})
}
func TestIfValue(t *testing.T) {
t.Run("true", func(t *testing.T) {
var condCalled, customCalled bool
var gotValue string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
IfValue(
"foo",
func(value, meta interface{}) bool {
condCalled = true
gotValue = value.(string)
return true
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err == nil {
t.Fatal("Diff succeeded; want error")
}
if got, want := err.Error(), "bad"; got != want {
t.Fatalf("wrong error message %q; want %q", got, want)
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotValue, "baz"; got != want {
t.Errorf("wrong value %q; want %q", got, want)
}
}
if !customCalled {
t.Error("customize callback was not called")
}
})
t.Run("false", func(t *testing.T) {
var condCalled, customCalled bool
var gotValue string
provider := testProvider(
map[string]*schema.Schema{
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
IfValue(
"foo",
func(value, meta interface{}) bool {
condCalled = true
gotValue = value.(string)
return false
},
func(d *schema.ResourceDiff, meta interface{}) error {
customCalled = true
return errors.New("bad")
},
),
)
_, err := testDiff(
provider,
map[string]string{
"foo": "bar",
},
map[string]string{
"foo": "baz",
},
)
if err != nil {
t.Fatalf("Diff error %q; want success", err.Error())
}
if !condCalled {
t.Error("condition callback was not called")
} else {
if got, want := gotValue, "baz"; got != want {
t.Errorf("wrong value %q; want %q", got, want)
}
}
if customCalled {
t.Error("customize callback was called (should not have been)")
}
})
}