diff --git a/internal/testing/mappings.go b/internal/testing/mappings.go new file mode 100644 index 000000000..493bd3ce3 --- /dev/null +++ b/internal/testing/mappings.go @@ -0,0 +1,38 @@ +// Copyright 2016-2022, 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 testing + +import ( + "os" + "path/filepath" +) + +type TestFileMapper struct { + Path string +} + +func (l *TestFileMapper) GetMapping(provider string) ([]byte, error) { + mappingPath := filepath.Join(l.Path, provider) + ".json" + + mappingBytes, err := os.ReadFile(mappingPath) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + return mappingBytes, nil +} diff --git a/pkg/tf2pulumi/convert/eject_test.go b/pkg/tf2pulumi/convert/eject_test.go index 55121babb..48210b8d9 100644 --- a/pkg/tf2pulumi/convert/eject_test.go +++ b/pkg/tf2pulumi/convert/eject_test.go @@ -22,18 +22,13 @@ import ( "testing" "github.com/blang/semver" + bridgetesting "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testing" "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -// Asserts that err is nil and if not stops the test -func stopOnError(t *testing.T, err error) { - if !assert.NoError(t, err) { - t.FailNow() - } -} - type testLoader struct { path string } @@ -75,24 +70,6 @@ func (l *testLoader) LoadPackageReference(pkg string, version *semver.Version) ( return schemaPackage.Reference(), nil } -type testMapper struct { - path string -} - -func (l *testMapper) GetMapping(provider string) ([]byte, error) { - mappingPath := filepath.Join(l.path, provider) + ".json" - - mappingBytes, err := os.ReadFile(mappingPath) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, err - } - - return mappingBytes, nil -} - func isTruthy(s string) bool { return s == "1" || strings.EqualFold(s, "true") } @@ -102,9 +79,9 @@ func TestEject(t *testing.T) { // Each folder in testdata has a pcl folder, we check that if we convert the hcl we get the expected pcl // You can regenerate the test data by running "PULUMI_ACCEPT=1 go test" in this folder (pkg/tf2pulumi/convert). testDir, err := filepath.Abs(filepath.Join("testdata")) - stopOnError(t, err) + require.NoError(t, err) infos, err := os.ReadDir(testDir) - stopOnError(t, err) + require.NoError(t, err) tests := make([]struct { name string @@ -124,7 +101,7 @@ func TestEject(t *testing.T) { } loader := &testLoader{path: filepath.Join(testDir, "schemas")} - mapper := &testMapper{path: filepath.Join(testDir, "mappings")} + mapper := &bridgetesting.TestFileMapper{Path: filepath.Join(testDir, "mappings")} for _, tt := range tests { tt := tt // avoid capturing loop variable in the closure @@ -136,9 +113,7 @@ func TestEject(t *testing.T) { pclPath := filepath.Join(tt.path, "pcl") project, program, err := Eject(hclPath, loader, mapper) - if !assert.NoError(t, err) { - return - } + require.NoError(t, err) // Assert the project name is as expected (the directory name) assert.Equal(t, tokens.PackageName(tt.name), project.Name) @@ -160,20 +135,14 @@ func TestEject(t *testing.T) { // If PULUMI_ACCEPT is set then clear the PCL folder and write the generated files out if isTruthy(os.Getenv("PULUMI_ACCEPT")) { err := os.RemoveAll(pclPath) - if !assert.NoError(t, err) { - t.FailNow() - } + require.NoError(t, err) err = os.Mkdir(pclPath, 0700) - if !assert.NoError(t, err) { - t.FailNow() - } + require.NoError(t, err) for filename, source := range program.Source() { // normalize windows newlines to unix ones expectedPcl := []byte(strings.Replace(source, "\r\n", "\n", -1)) err := os.WriteFile(filepath.Join(pclPath, filename), expectedPcl, 0600) - if !assert.NoError(t, err) { - t.FailNow() - } + require.NoError(t, err) } } diff --git a/pkg/tf2pulumi/convert/testdata/mappings/simple.json b/pkg/tf2pulumi/convert/testdata/mappings/simple.json index 4554f9ab9..ea9685bc2 100644 --- a/pkg/tf2pulumi/convert/testdata/mappings/simple.json +++ b/pkg/tf2pulumi/convert/testdata/mappings/simple.json @@ -4,10 +4,12 @@ "dataSources": { "simple_data_source": { "input_one": { - "type": 4 + "type": 4, + "optional": true }, "input_two": { - "type": 2 + "type": 2, + "optional": true }, "result": { "type": 4, @@ -18,10 +20,12 @@ "resources": { "simple_resource": { "input_one": { - "type": 4 + "type": 4, + "optional": true }, "input_two": { - "type": 2 + "type": 2, + "optional": true }, "result": { "type": 4, @@ -33,40 +37,12 @@ }, "dataSources": { "simple_data_source": { - "tok": "simple:index:data_source", - "fields": { - "input_one": { - "name": "inputOne", - "type": "string" - }, - "input_two": { - "name": "inputTwo", - "type": "integer" - }, - "result": { - "name": "result", - "type": "string" - } - } + "tok": "simple:index:data_source" } }, "resources": { "simple_resource": { - "tok": "simple:index:resource", - "fields": { - "input_one": { - "name": "inputOne", - "type": "string" - }, - "input_two": { - "name": "inputTwo", - "type": "integer" - }, - "result": { - "name": "result", - "type": "string" - } - } + "tok": "simple:index:resource" } } } \ No newline at end of file diff --git a/pkg/tf2pulumi/convert/testdata/schemas/README.md b/pkg/tf2pulumi/convert/testdata/schemas/README.md new file mode 100644 index 000000000..72de77477 --- /dev/null +++ b/pkg/tf2pulumi/convert/testdata/schemas/README.md @@ -0,0 +1,2 @@ +These files are auto-generated and checked via the tfgen/Test_GenerateTestDataSchemas test. +DO NOT EDIT BY HAND! \ No newline at end of file diff --git a/pkg/tf2pulumi/convert/testdata/schemas/simple.json b/pkg/tf2pulumi/convert/testdata/schemas/simple.json index b93e91557..4742a0422 100644 --- a/pkg/tf2pulumi/convert/testdata/schemas/simple.json +++ b/pkg/tf2pulumi/convert/testdata/schemas/simple.json @@ -1,43 +1,101 @@ { - "version": "0.0.1", "name": "simple", - "functions":{ - "simple:index:data_source": { - "inputs": { - "type": "object", - "properties": { - "inputOne": { - "type": "string" - }, - "inputTwo": { - "type": "integer" - } - } - }, - "outputs": { - "properties": { - "result": { - "type": "string" - } - }, - "type": "object" - } + "attribution": "This Pulumi package is based on the [`simple` Terraform Provider](https://github.com/terraform-providers/terraform-provider-simple).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true, + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-simple)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-simple` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-simple` repo](https://github.com/terraform-providers/terraform-provider-simple/issues)." + }, + "python": { + "compatibility": "tfbridge20", + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-simple)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-simple` repo](/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-simple` repo](https://github.com/terraform-providers/terraform-provider-simple/issues)." } }, + "config": {}, + "provider": { + "description": "The provider type for the simple package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n" + }, "resources": { "simple:index:resource": { - "inputProperties": { + "properties": { "inputOne": { "type": "string" }, "inputTwo": { "type": "integer" + }, + "result": { + "type": "string" } }, - "properties": { - "result": { + "required": [ + "result" + ], + "inputProperties": { + "inputOne": { "type": "string" + }, + "inputTwo": { + "type": "integer" } + }, + "stateInputs": { + "description": "Input properties used for looking up and filtering resource resources.\n", + "properties": { + "inputOne": { + "type": "string" + }, + "inputTwo": { + "type": "integer" + }, + "result": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "functions": { + "simple:index:data_source": { + "inputs": { + "description": "A collection of arguments for invoking data_source.\n", + "properties": { + "inputOne": { + "type": "string" + }, + "inputTwo": { + "type": "integer" + } + }, + "type": "object" + }, + "outputs": { + "description": "A collection of values returned by data_source.\n", + "properties": { + "id": { + "type": "string", + "description": "The provider-assigned unique ID for this managed resource.\n" + }, + "inputOne": { + "type": "string" + }, + "inputTwo": { + "type": "integer" + }, + "result": { + "type": "string" + } + }, + "type": "object", + "required": [ + "result", + "id" + ] } } } diff --git a/pkg/tfgen/generate_test.go b/pkg/tfgen/generate_test.go index 05596bc54..b74998454 100644 --- a/pkg/tfgen/generate_test.go +++ b/pkg/tfgen/generate_test.go @@ -15,13 +15,22 @@ package tfgen import ( + "io" + "os" + "path/filepath" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + bridgetesting "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testing" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tf2pulumi/il" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" shimv1 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v1" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_DeprecationFromTFSchema(t *testing.T) { @@ -90,3 +99,36 @@ func Test_ForceNew(t *testing.T) { }) } } + +func Test_GenerateTestDataSchemas(t *testing.T) { + // This is to assert that all the schemas we save in tf2pulumi/convert/testdata/schemas, match up with the + // mapping files in tf2pulumi/convert/testdata/mappings. Add in the use of PULUMI_ACCEPT and it means you + // don't have to manually write schemas, just mappings for tests. + + testDir, err := filepath.Abs(filepath.Join("..", "tf2pulumi", "convert", "testdata")) + require.NoError(t, err) + mappingsPath := filepath.Join(testDir, "mappings") + schemasPath := filepath.Join(testDir, "schemas") + mapper := &bridgetesting.TestFileMapper{Path: mappingsPath} + providerInfoSource := il.NewMapperProviderInfoSource(mapper) + + nilSink := diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{ + Color: colors.Never, + }) + + // Generate the schemas from the mappings + infos, err := os.ReadDir(mappingsPath) + require.NoError(t, err) + for _, info := range infos { + // Strip off the .json part to make the package name + pkg := strings.Replace(info.Name(), filepath.Ext(info.Name()), "", -1) + provInfo, err := providerInfoSource.GetProviderInfo("", "", pkg, "") + require.NoError(t, err) + + schema, err := GenerateSchema(*provInfo, nilSink) + require.NoError(t, err) + + schemaPath := filepath.Join(schemasPath, pkg+".json") + bridgetesting.AssertEqualsJSONFile(t, schemaPath, schema) + } +}