Skip to content

Commit

Permalink
PF integration tests using pulumitest (#2186)
Browse files Browse the repository at this point in the history
This adds some utility functions which allow simple integration testing
in the PF bridge, similar to the tests in pkg:
https://github.com/pulumi/pulumi-terraform-bridge/blob/f317dc4a503d3430fbe13f3af964207f0a17616e/pkg/tests/schema_pulumi_test.go#L19

Builds on `pulumitest`, the `providerbuilder` tools and the `pulcheck`
code under `pkg` originally adapted from the cross tests.

Allows integration tests to be written against PF, where the schema and
the yaml program are specified concisely in the test and pulumitest is
used for the actions which allows quite a bit of freedom in expressing
pulumi operations.
  • Loading branch information
VenelinMartinov authored Jul 22, 2024
1 parent 59ab72c commit c57aebd
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
42 changes: 42 additions & 0 deletions pf/tests/schema_and_program_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package tfbridgetests

import (
"testing"

pschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/pulumi/pulumi-terraform-bridge/pf/tests/internal/providerbuilder"
)

func TestBasic(t *testing.T) {
provBuilder := providerbuilder.Provider{
TypeName: "prov",
Version: "0.0.1",
ProviderSchema: pschema.Schema{},
AllResources: []providerbuilder.Resource{
{
Name: "test",
ResourceSchema: rschema.Schema{
Attributes: map[string]rschema.Attribute{
"s": rschema.StringAttribute{Optional: true},
},
},
},
},
}

prov := bridgedProvider(&provBuilder)

program := `
name: test
runtime: yaml
resources:
mainRes:
type: prov:index:Test
properties:
s: "hello"`

pt := pulCheck(t, prov, program)

pt.Up()
}
97 changes: 97 additions & 0 deletions pf/tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,31 @@ package tfbridgetests

import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

"github.com/pulumi/providertest/providers"
"github.com/pulumi/providertest/pulumitest"
"github.com/pulumi/providertest/pulumitest/opttest"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"

"github.com/pulumi/pulumi-terraform-bridge/pf/tests/internal/providerbuilder"
"github.com/pulumi/pulumi-terraform-bridge/pf/tfbridge"
tfbridge0 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/tokens"
)

func newProviderServer(t *testing.T, info tfbridge0.ProviderInfo) pulumirpc.ResourceProviderServer {
Expand All @@ -41,3 +58,83 @@ func newMuxedProviderServer(t *testing.T, info tfbridge0.ProviderInfo) pulumirpc
require.NoError(t, err)
return p
}

func ensureProviderValid(prov *providerbuilder.Provider) {
for i := range prov.AllResources {
r := &prov.AllResources[i]
if r.ResourceSchema.Attributes["id"] == nil {
r.ResourceSchema.Attributes["id"] = rschema.StringAttribute{Computed: true}
}
if r.CreateFunc == nil {
r.CreateFunc = func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
resp.State = tfsdk.State(req.Config)
resp.State.SetAttribute(ctx, path.Root("id"), "test-id")
}
}
}

}

func bridgedProvider(prov *providerbuilder.Provider) info.Provider {
ensureProviderValid(prov)
shimProvider := tfbridge.ShimProvider(prov)

provider := tfbridge0.ProviderInfo{
P: shimProvider,
Name: prov.TypeName,
Version: "0.0.1",
MetadataInfo: &tfbridge0.MetadataInfo{},
}

provider.MustComputeTokens(tokens.SingleModule(prov.TypeName, "index", tokens.MakeStandard(prov.TypeName)))

return provider
}

func startPulumiProvider(t *testing.T, providerInfo tfbridge0.ProviderInfo) (*rpcutil.ServeHandle, error) {
prov := newProviderServer(t, providerInfo)

handle, err := rpcutil.ServeWithOptions(rpcutil.ServeOptions{
Init: func(srv *grpc.Server) error {
pulumirpc.RegisterResourceProviderServer(srv, prov)
return nil
},
})
if err != nil {
return nil, fmt.Errorf("rpcutil.ServeWithOptions failed: %w", err)
}

return &handle, nil
}

func skipUnlessLinux(t *testing.T) {
if ci, ok := os.LookupEnv("CI"); ok && ci == "true" && !strings.Contains(strings.ToLower(runtime.GOOS), "linux") {
// TODO[pulumi/pulumi-terraform-bridge#2221]
t.Skip("Skipping on non-Linux platforms")
}
}

func pulCheck(t *testing.T, bridgedProvider info.Provider, program string) *pulumitest.PulumiTest {
skipUnlessLinux(t)
puwd := t.TempDir()
p := filepath.Join(puwd, "Pulumi.yaml")

err := os.WriteFile(p, []byte(program), 0o600)
require.NoError(t, err)

opts := []opttest.Option{
opttest.Env("DISABLE_AUTOMATIC_PLUGIN_ACQUISITION", "true"),
opttest.TestInPlace(),
opttest.SkipInstall(),
opttest.AttachProvider(
bridgedProvider.Name,
func(ctx context.Context, pt providers.PulumiTest) (providers.Port, error) {
handle, err := startPulumiProvider(t, bridgedProvider)
require.NoError(t, err)
return providers.Port(handle.Port), nil
},
),
}

return pulumitest.NewPulumiTest(t, puwd, opts...)
}

0 comments on commit c57aebd

Please sign in to comment.