diff --git a/infer/resource_test.go b/infer/resource_test.go index bb0132b7..d2080662 100644 --- a/infer/resource_test.go +++ b/infer/resource_test.go @@ -16,15 +16,107 @@ package infer import ( "context" + "reflect" "testing" - provider "github.com/pulumi/pulumi-go-provider" "github.com/pulumi/pulumi/sdk/v3/go/common/diag" r "github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "pgregory.net/rapid" + + provider "github.com/pulumi/pulumi-go-provider" + "github.com/pulumi/pulumi-go-provider/infer/internal/ende" + rRapid "github.com/pulumi/pulumi-go-provider/internal/rapid/resource" ) +// testGetDependencies runs a property test that asserts on the flow between inputs and +// outputs for some I, O ∈ GoType and ∀ (old,new) ∈ I, out ∈ O. +func testGetDependencies[I any, O any](t *testing.T, + wire func(FieldSelector, *I, *O), + assert func(t *testing.T, oldInput, newInput, output r.PropertyMap), +) { + var i I + var o O + wireDeps := func(f FieldSelector) { + if wire != nil { + wire(f, &i, &o) + } + } + setDeps, err := getDependenciesRaw( + &i, &o, wireDeps, + false, /*isCreate*/ + true /*isPreview*/) + require.NoError(t, err) + + inputT := rapid.Just(reflect.TypeOf(i)) + outputT := rapid.Just(reflect.TypeOf(o)) + + getMap := func(t rRapid.Typed) r.PropertyMap { + return t.Value.ObjectValue() + } + + rapid.Check(t, func(rt *rapid.T) { + oldInput := rapid.Map(rRapid.ValueOf(inputT), getMap). + Draw(rt, "oldInput") + newInput := rapid.Map(rRapid.ValueOf(inputT), getMap). + Draw(rt, "newInput") + output := rapid.Map(rRapid.ValueOf(outputT), getMap). + Draw(rt, "output") + + setDeps(oldInput, newInput, output) + + assert(t, oldInput, newInput, output) + }) +} + +func TestDefaultDependencies(t *testing.T) { + t.Parallel() + type input struct { + I1 string `pulumi:"i1"` + I2 *int `pulumi:"i2,optional"` + I3 map[string]string `pulumi:"i3"` + } + + type output struct { + input + + O1 *string `pulumi:"o1,optional"` + O2 float64 `pulumi:"o2"` + O3 map[string]int `pulumi:"o2"` + } + + assert := func(t *testing.T, oldInput, newInput, output r.PropertyMap) { + if newInput.ContainsUnknowns() { + for k, v := range output { + if newV, ok := newInput[k]; ok && + ende.DeepEquals(newV, v) { + continue + } + assert.True(t, ende.IsComputed(v), + "key: %q, value: %#v", string(k), v) + } + } else if !ende.DeepEquals( + r.NewObjectProperty(oldInput), + r.NewObjectProperty(newInput)) { + for k, v := range output { + assert.True(t, ende.IsComputed(v), + "key: %q", string(k)) + } + } + + for k, v := range output { + // An input of the same name is secret, so this should be too. + if ende.IsSecret(newInput[k]) { + assert.Truef(t, ende.IsSecret(v), + "key: %q", string(k)) + } + } + } + + testGetDependencies[input, output](t, nil, assert) +} + func TestFieldGenerator(t *testing.T) { t.Parallel() type args struct {