Skip to content

Commit

Permalink
[PF] Property based Configure tests
Browse files Browse the repository at this point in the history
  • Loading branch information
iwahbe committed Oct 16, 2024
1 parent 678d0fd commit a77c9b8
Show file tree
Hide file tree
Showing 12 changed files with 1,550 additions and 25 deletions.
2 changes: 1 addition & 1 deletion pf/tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tests v0.0.0-00010101000000-000000000000
github.com/stretchr/testify v1.9.0
github.com/terraform-providers/terraform-provider-random/randomshim v0.0.0
pgregory.net/rapid v1.1.0
)

require (
Expand Down Expand Up @@ -85,7 +86,6 @@ require (
gotest.tools v2.2.0+incompatible // indirect
gotest.tools/v3 v3.0.3 // indirect
mvdan.cc/gofumpt v0.5.0 // indirect
pgregory.net/rapid v0.6.1 // indirect
)

replace (
Expand Down
4 changes: 2 additions & 2 deletions pf/tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3173,8 +3173,8 @@ modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E=
mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js=
pgregory.net/rapid v0.6.1 h1:4eyrDxyht86tT4Ztm+kvlyNBLIk071gR+ZQdhphc9dQ=
pgregory.net/rapid v0.6.1/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
Expand Down
47 changes: 33 additions & 14 deletions pf/tests/internal/cross-tests/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ import (
// }
//
// For details on the test itself, see [Configure].
func MakeConfigure(schema schema.Schema, tfConfig map[string]cty.Value, puConfig resource.PropertyMap) func(t *testing.T) {
func MakeConfigure(
schema schema.Schema, tfConfig map[string]cty.Value, puConfig resource.PropertyMap,
options ...ConfigureOption,
) func(t *testing.T) {
return func(t *testing.T) {
t.Parallel()
Configure(t, schema, tfConfig, puConfig)
Configure(t, schema, tfConfig, puConfig, options...)
}
}

Expand Down Expand Up @@ -81,9 +84,17 @@ func MakeConfigure(schema schema.Schema, tfConfig map[string]cty.Value, puConfig
// +--------------------+ +---------------------+
//
// Configure should be safe to run in parallel.
func Configure(t *testing.T, schema schema.Schema, tfConfig map[string]cty.Value, puConfig resource.PropertyMap) {
func Configure(
t TestingT, schema schema.Schema, tfConfig map[string]cty.Value, puConfig resource.PropertyMap,
options ...ConfigureOption,
) {
skipUnlessLinux(t)

var opts configureOptions
for _, o := range options {
o(&opts)
}

// By default, logs only show when they are on a failed test. By logging to
// topLevelT, we can log items to be shown if downstream tests fail.
topLevelT := t
Expand All @@ -103,8 +114,8 @@ func Configure(t *testing.T, schema schema.Schema, tfConfig map[string]cty.Value
}

var tfOutput, puOutput tfsdk.Config
t.Run("tf", func(t *testing.T) {
defer propageteSkip(topLevelT, t)

withAugment(t, func(t augmentedT) { // --- Run Terraform Provider ---
var hcl bytes.Buffer
err := crosstests.WritePF(&hcl).Provider(schema, providerName, tfConfig)
require.NoError(t, err)
Expand All @@ -120,13 +131,12 @@ resource "` + providerName + `_res" "res" {}

driver.Write(t, hcl.String())
plan, err := driver.Plan(t)
require.NoError(t, err)
require.NoError(t, err, "failed to generate TF plan")
err = driver.Apply(t, plan)
require.NoError(t, err)
})

t.Run("bridged", func(t *testing.T) {
defer propageteSkip(topLevelT, t)
withAugment(t, func(t augmentedT) { // --- Run Pulumi Provider ---
dir := t.TempDir()

pulumiYaml := map[string]any{
Expand Down Expand Up @@ -187,11 +197,20 @@ resource "` + providerName + `_res" "res" {}
contract.Ignore(test.Up(t)) // Assert that the update succeeded, but not the result.
})

skipCompare := t.Failed() || t.Skipped()
t.Run("compare", func(t *testing.T) {
if skipCompare {
t.Skipf("skipping since earlier steps did not complete")
}
// --- Compare results -----------------------------
if opts.testEqual != nil {
opts.testEqual(t, tfOutput, puOutput)
} else {
assert.Equal(t, tfOutput, puOutput)
})
}
}

type ConfigureOption func(*configureOptions)

type configureOptions struct {
testEqual func(t TestingT, tfOutput, puOutput tfsdk.Config)
}

func WithConfigureEqual(equal func(t TestingT, tfOutput, puOutput tfsdk.Config)) ConfigureOption {
return func(opts *configureOptions) { opts.testEqual = equal }
}
71 changes: 63 additions & 8 deletions pf/tests/internal/cross-tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,26 @@ import (
"os"
"runtime"
"strings"
"testing"
"time"

"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/stretchr/testify/require"
)

func propageteSkip(parent, child *testing.T) {
if child.Skipped() {
parent.Skipf("skipping due to skipped child test")
}
type TestingT interface {
Skip(args ...any)
Failed() bool
Errorf(format string, args ...any)
Name() string
Log(...any)
Logf(string, ...any)
Fail()
FailNow()
Helper()
}

type testLogSink struct{ t *testing.T }
type testLogSink struct{ t TestingT }

func (s testLogSink) Log(_ context.Context, sev diag.Severity, urn resource.URN, msg string) error {
return s.log("LOG", sev, urn, msg)
Expand All @@ -50,7 +56,7 @@ func (s testLogSink) log(kind string, sev diag.Severity, urn resource.URN, msg s
return nil
}

func convertResourceValue(t *testing.T, properties resource.PropertyMap) map[string]any {
func convertResourceValue(t TestingT, properties resource.PropertyMap) map[string]any {
var convertValue func(resource.PropertyValue) (any, bool)
convertValue = func(v resource.PropertyValue) (any, bool) {
if v.IsComputed() {
Expand Down Expand Up @@ -81,8 +87,57 @@ func convertResourceValue(t *testing.T, properties resource.PropertyMap) map[str
return properties.MapRepl(nil, convertValue)
}

func skipUnlessLinux(t *testing.T) {
func skipUnlessLinux(t TestingT) {
if ci, ok := os.LookupEnv("CI"); ok && ci == "true" && !strings.Contains(strings.ToLower(runtime.GOOS), "linux") {
t.Skip("Skipping on non-Linux platforms as our CI does not yet install Terraform CLI required for these tests")
}
}

type augmentedT interface {
TestingT
Cleanup(func())
Deadline() (time.Time, bool)
TempDir() string
}

func withAugment(t TestingT, f func(t augmentedT)) {
c := withAugmentedT{TestingT: t}
defer c.all()
f(&c)
}

type withAugmentedT struct {
TestingT
tasks []func()
}

// TempDir returns a temporary directory for the test to use.
// The directory is automatically removed when the test and
// all its subtests complete.
// Each subsequent call to t.TempDir returns a unique directory;
// if the directory creation fails, TempDir terminates the test by calling Fatal.
func (t *withAugmentedT) TempDir() string {
name := t.Name()
name = strings.ReplaceAll(name, "#", "")
name = strings.ReplaceAll(name, string(os.PathSeparator), "")
dir, err := os.MkdirTemp("", name)
require.NoError(t, err)
return dir
}

func (t *withAugmentedT) Cleanup(f func()) {
t.tasks = append(t.tasks, f)
}

func (t *withAugmentedT) Deadline() (time.Time, bool) {
return time.Time{}, false
}

func (t *withAugmentedT) all() {
for i := len(t.tasks) - 1; i >= 0; i-- {
v := t.tasks[i]
if v != nil {
v()
}
}
}
46 changes: 46 additions & 0 deletions pf/tests/internal/cross-tests/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2016-2024, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package crosstests

import (
"testing"

"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/stretchr/testify/assert"
)

func TestConvertResourceValue(t *testing.T) {
t.Parallel()
tests := []struct {
input resource.PropertyMap
expected map[string]any
}{
{
input: resource.PropertyMap{
"a": resource.NewProperty(resource.PropertyMap{}),
},
expected: map[string]any{
"a": map[string]any{},
},
},
}

for _, tt := range tests {
t.Run("", func(t *testing.T) {
actual := convertResourceValue(t, tt.input)
assert.Equal(t, tt.expected, actual)
})
}
}
Loading

0 comments on commit a77c9b8

Please sign in to comment.