package test import ( "os" "os/exec" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/pulumi/pulumi/pkg/v3/codegen" "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" ) type ThenFunc func(t *testing.T, testDir string) type sdkTest struct { Directory string Description string SkipCompileCheck codegen.StringSet Then map[string]ThenFunc } const ( nodejs = "nodejs" dotnet = "dotnet" golang = "go" ) var sdkTests = []sdkTest{ { Directory: "input-collision", Description: "Schema with types that could potentially produce collisions (go).", }, { Directory: "dash-named-schema", Description: "Simple schema with a two part name (foo-bar)", }, { Directory: "external-resource-schema", Description: "External resource schema", SkipCompileCheck: codegen.NewStringSet(nodejs, golang), }, { Directory: "nested-module", Description: "Nested module", SkipCompileCheck: codegen.NewStringSet(dotnet, nodejs), }, { Directory: "nested-module-thirdparty", Description: "Third-party nested module", SkipCompileCheck: codegen.NewStringSet(dotnet, nodejs), }, { Directory: "plain-schema-gh6957", Description: "Repro for #6957", }, { Directory: "resource-args-python", Description: "Resource args with same named resource and type", Then: map[string]ThenFunc{ "go": func(t *testing.T, testDir string) { cmd := exec.Command("go", "test", "./...") cmd.Dir = filepath.Join(testDir, "go-program") out, err := cmd.CombinedOutput() if !assert.NoError(t, err) { t.Logf("output: %v", string(out)) } }, }, }, { Directory: "simple-enum-schema", Description: "Simple schema with enum types", }, { Directory: "simple-plain-schema", Description: "Simple schema with plain properties", }, { Directory: "simple-plain-schema-with-root-package", Description: "Simple schema with root package set", }, { Directory: "simple-resource-schema", Description: "Simple schema with local resource properties", }, { Directory: "simple-resource-schema-custom-pypackage-name", Description: "Simple schema with local resource properties and custom Python package name", }, { Directory: "simple-methods-schema", Description: "Simple schema with methods", SkipCompileCheck: codegen.NewStringSet(nodejs, dotnet, golang), }, { Directory: "simple-yaml-schema", Description: "Simple schema encoded using YAML", }, { Directory: "provider-config-schema", Description: "Simple provider config schema", SkipCompileCheck: codegen.NewStringSet(dotnet), }, { Directory: "replace-on-change", Description: "Simple use of replaceOnChange in schema", SkipCompileCheck: codegen.NewStringSet(golang), }, { Directory: "resource-property-overlap", Description: "A resource with the same name as it's property", SkipCompileCheck: codegen.NewStringSet(dotnet, nodejs), }, { Directory: "hyphen-url", Description: "A resource url with a hyphen in it's path", }, } type checkPackageSignature = func(t *testing.T, pwd string) // TestSDKCodegen runs the complete set of SDK code generation tests against a particular language's code // generator. It also verifies that the generated code is structurally sound. // // An SDK code generation test consists of a schema and a set of expected outputs for each language. Each test is // structured as a directory that contains that information: // // test-directory/ // schema.(json|yaml) // language-0 // ... // language-n // // The schema is the only piece that must be manually authored. Once the schema has been written, the expected outputs // can be generated by running `PULUMI_ACCEPT=true go test ./..." from the `pkg/codegen` directory. //nolint: revive func TestSDKCodegen(t *testing.T, language string, genPackage GenPkgSignature, checkPackage checkPackageSignature) { testDir := filepath.Join("..", "internal", "test", "testdata") for _, tt := range sdkTests { t.Run(tt.Description, func(t *testing.T) { dirPath := filepath.Join(testDir, filepath.FromSlash(tt.Directory)) schemaPath := filepath.Join(dirPath, "schema.json") if _, err := os.Stat(schemaPath); err != nil && os.IsNotExist(err) { schemaPath = filepath.Join(dirPath, "schema.yaml") } files, err := GeneratePackageFilesFromSchema(schemaPath, genPackage) require.NoError(t, err) // Check output is valid code (will type-check). If code will not // type-check, we don't allow the user to run PULUMI_ACCEPT=true and // replace the test files. if !tt.SkipCompileCheck.Has(language) { typeCheckPath := filepath.Join(dirPath, "typecheck") langTypeCheckPath := filepath.Join(typeCheckPath, language) contract.IgnoreError(os.RemoveAll(langTypeCheckPath)) WriteTestFiles(t, typeCheckPath, language, files) checkPackage(t, langTypeCheckPath) } if !RewriteFilesWhenPulumiAccept(t, dirPath, language, files) { expectedFiles, err := LoadBaseline(dirPath, language) require.NoError(t, err) if !ValidateFileEquality(t, files, expectedFiles) { return } } if then, ok := tt.Then[language]; ok { then(t, dirPath) } }) } }