2022-11-03 20:30:35 +00:00
|
|
|
// 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.
|
|
|
|
|
2023-01-30 19:30:30 +00:00
|
|
|
//go:build (nodejs || all) && !xplatform_acceptance
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
package ints
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2023-02-28 17:06:15 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/engine"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/secrets/cloud"
|
2021-05-14 22:00:09 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/secrets/passphrase"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
|
|
ptesting "github.com/pulumi/pulumi/sdk/v3/go/common/testing"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-09-02 16:11:15 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2024-02-16 08:25:12 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2020-09-02 16:11:15 +00:00
|
|
|
)
|
|
|
|
|
Fix #3982, missing output which lacks newlines (#8671)
* Fix #3982, missing output which lacks newlines
If the last line printed to stdout or stderr was missing a
terminating newline, it would go entirely missing (in all languages).
The reason for this is a bug in the engine's handling of plugin
outputs: Go's Reader.ReadString('\n') returns a string containing what
was read and/or an error; if the string terminated in a '\n', the
error is nil, and the entire line is returned; if the stream ends,
however, a non-nil error is returned *and* what was read is returned,
even though it wasn't terminated in a newline. The fix is simple:
instead of ignoring that text, we use it, and *then* exit the read-loop.
Also added some test cases since this is subtle and easy to regress.
* Add a changelog entry
2022-01-03 22:39:10 +00:00
|
|
|
// TestPrintfNodeJS tests that we capture stdout and stderr streams properly, even when the last line lacks an \n.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
Fix #3982, missing output which lacks newlines (#8671)
* Fix #3982, missing output which lacks newlines
If the last line printed to stdout or stderr was missing a
terminating newline, it would go entirely missing (in all languages).
The reason for this is a bug in the engine's handling of plugin
outputs: Go's Reader.ReadString('\n') returns a string containing what
was read and/or an error; if the string terminated in a '\n', the
error is nil, and the entire line is returned; if the stream ends,
however, a non-nil error is returned *and* what was read is returned,
even though it wasn't terminated in a newline. The fix is simple:
instead of ignoring that text, we use it, and *then* exit the read-loop.
Also added some test cases since this is subtle and easy to regress.
* Add a changelog entry
2022-01-03 22:39:10 +00:00
|
|
|
func TestPrintfNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("printf", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: printfTestValidation,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
// Tests emitting many engine events doesn't result in a performance problem.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestEngineEventPerf(t *testing.T) {
|
2021-11-15 20:17:20 +00:00
|
|
|
t.Skip() // TODO[pulumi/pulumi#7883]
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
// Prior to pulumi/pulumi#2303, a preview or update would take ~40s.
|
|
|
|
// Since then, it should now be down to ~4s, with additional padding,
|
|
|
|
// since some Travis machines (especially the macOS ones) seem quite slow
|
|
|
|
// to begin with.
|
|
|
|
benchmarkEnforcer := &assertPerfBenchmark{
|
|
|
|
T: t,
|
|
|
|
MaxPreviewDuration: 8 * time.Second,
|
|
|
|
MaxUpdateDuration: 8 * time.Second,
|
|
|
|
}
|
|
|
|
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "ee_perf",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ReportStats: benchmarkEnforcer,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestEngineEvents ensures that the test framework properly records and reads engine events.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestEngineEvents(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "single_resource",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure that we have a non-empty list of events.
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
|
|
|
|
// Ensure that we have two "ResourcePre" events: one for the stack and one for our resource.
|
|
|
|
preEventResourceTypes := []string{}
|
|
|
|
for _, e := range stackInfo.Events {
|
|
|
|
if e.ResourcePreEvent != nil {
|
|
|
|
preEventResourceTypes = append(preEventResourceTypes, e.ResourcePreEvent.Metadata.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Equal(t, 2, len(preEventResourceTypes))
|
|
|
|
assert.Contains(t, preEventResourceTypes, "pulumi:pulumi:Stack")
|
|
|
|
assert.Contains(t, preEventResourceTypes, "pulumi-nodejs:dynamic:Resource")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-09-22 13:46:48 +00:00
|
|
|
// TestProjectMainNodejs tests out the ability to override the main entrypoint.
|
|
|
|
func TestProjectMainNodejs(t *testing.T) {
|
2020-09-02 16:11:15 +00:00
|
|
|
test := integration.ProgramTestOptions{
|
2022-09-22 13:46:48 +00:00
|
|
|
Dir: "project_main/nodejs",
|
2020-09-02 16:11:15 +00:00
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Simple runtime validation that just ensures the checkpoint was written and read.
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
integration.ProgramTest(t, &test)
|
|
|
|
|
2021-04-09 04:39:52 +00:00
|
|
|
t.Run("AbsolutePath", func(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory("project_main_abs")
|
2021-04-09 04:39:52 +00:00
|
|
|
|
|
|
|
// write a new Pulumi.yaml using the absolute path of the environment as "main"
|
|
|
|
yamlPath := filepath.Join(e.RootPath, "Pulumi.yaml")
|
|
|
|
absYamlContents := fmt.Sprintf(
|
|
|
|
"name: project_main_abs\ndescription: A program with an absolute entry point\nruntime: nodejs\nmain: %s\n",
|
|
|
|
e.RootPath,
|
|
|
|
)
|
|
|
|
t.Logf("writing new Pulumi.yaml: \npath: %s\ncontents:%s", yamlPath, absYamlContents)
|
2023-11-28 16:43:48 +00:00
|
|
|
if err := os.WriteFile(yamlPath, []byte(absYamlContents), 0o600); err != nil {
|
2021-04-09 04:39:52 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
2020-09-02 16:11:15 +00:00
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
|
|
|
e.RunCommand("pulumi", "stack", "init", "main-abs")
|
2021-04-09 04:39:52 +00:00
|
|
|
e.RunCommand("pulumi", "preview")
|
2020-09-02 16:11:15 +00:00
|
|
|
e.RunCommand("pulumi", "stack", "rm", "--yes")
|
|
|
|
})
|
|
|
|
|
2021-04-09 04:39:52 +00:00
|
|
|
t.Run("ParentFolder", func(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory("project_main_parent")
|
2021-04-09 04:39:52 +00:00
|
|
|
|
|
|
|
// yarn link first
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
// then virtually change directory to the location of the nested Pulumi.yaml
|
|
|
|
e.CWD = filepath.Join(e.RootPath, "foo", "bar")
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
|
|
|
e.RunCommand("pulumi", "stack", "init", "main-parent")
|
2021-04-09 04:39:52 +00:00
|
|
|
e.RunCommand("pulumi", "preview")
|
2020-09-02 16:11:15 +00:00
|
|
|
e.RunCommand("pulumi", "stack", "rm", "--yes")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackProjectName ensures we can read the Pulumi stack and project name from within the program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackProjectName(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2022-09-14 02:34:01 +00:00
|
|
|
RequireService: true,
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
Dir: "stack_project_name",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRemoveWithResourcesBlocked(t *testing.T) {
|
|
|
|
if os.Getenv("PULUMI_ACCESS_TOKEN") == "" {
|
|
|
|
t.Skipf("Skipping: PULUMI_ACCESS_TOKEN is not set")
|
|
|
|
}
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
stackName, err := resource.NewUniqueHex("rm-test-", 8, -1)
|
|
|
|
contract.AssertNoErrorf(err, "resource.NewUniqueHex should not fail with no maximum length is set")
|
|
|
|
|
|
|
|
e.ImportDirectory("single_resource")
|
|
|
|
e.RunCommand("pulumi", "stack", "init", stackName)
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
e.RunCommand("pulumi", "up", "--non-interactive", "--yes", "--skip-preview")
|
|
|
|
_, stderr := e.RunCommandExpectError("pulumi", "stack", "rm", "--yes")
|
|
|
|
assert.Contains(t, stderr, "--force")
|
|
|
|
e.RunCommand("pulumi", "destroy", "--skip-preview", "--non-interactive", "--yes")
|
|
|
|
e.RunCommand("pulumi", "stack", "rm", "--yes")
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackOutputs ensures we can export variables from a stack and have them get recorded as outputs.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackOutputsNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("stack_outputs", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure the checkpoint contains a single resource, the Stack, with two outputs.
|
|
|
|
fmt.Printf("Deployment: %v", stackInfo.Deployment)
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
if assert.Equal(t, 1, len(stackInfo.Deployment.Resources)) {
|
|
|
|
stackRes := stackInfo.Deployment.Resources[0]
|
|
|
|
assert.NotNil(t, stackRes)
|
|
|
|
assert.Equal(t, resource.RootStackType, stackRes.URN.Type())
|
|
|
|
assert.Equal(t, 0, len(stackRes.Inputs))
|
|
|
|
assert.Equal(t, 2, len(stackRes.Outputs))
|
|
|
|
assert.Equal(t, "ABC", stackRes.Outputs["xyz"])
|
|
|
|
assert.Equal(t, float64(42), stackRes.Outputs["foo"])
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-25 22:37:46 +00:00
|
|
|
// TestStackOutputsProgramErrorNodeJS tests that when a program error occurs, we update any
|
|
|
|
// updated stack outputs, but otherwise leave others untouched.
|
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestStackOutputsProgramErrorNodeJS(t *testing.T) {
|
|
|
|
d := filepath.Join("stack_outputs_program_error", "nodejs")
|
|
|
|
|
|
|
|
validateOutputs := func(
|
|
|
|
expected map[string]interface{},
|
|
|
|
) func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
return func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, expected, stackInfo.RootResource.Outputs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join(d, "step1"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: validateOutputs(map[string]interface{}{
|
|
|
|
"xyz": "ABC",
|
|
|
|
"foo": float64(42),
|
|
|
|
}),
|
|
|
|
EditDirs: []integration.EditDir{
|
|
|
|
{
|
|
|
|
Dir: filepath.Join(d, "step2"),
|
|
|
|
Additive: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
// A program error in TypeScript means we won't get any new stack outputs from the module exports,
|
|
|
|
// so we expect the values to remain the same.
|
|
|
|
ExtraRuntimeValidation: validateOutputs(map[string]interface{}{
|
|
|
|
"xyz": "ABC",
|
|
|
|
"foo": float64(42),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackOutputsResourceErrorNodeJS ensures that prior stack outputs aren't overwritten when a
|
|
|
|
// resource operation error occurs during an update.
|
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestStackOutputsResourceErrorNodeJS(t *testing.T) {
|
|
|
|
d := filepath.Join("stack_outputs_resource_error", "nodejs")
|
|
|
|
|
|
|
|
validateOutputs := func(
|
|
|
|
expected map[string]interface{},
|
|
|
|
) func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
return func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, expected, stackInfo.RootResource.Outputs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join(d, "step1"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
LocalProviders: []integration.LocalDependency{
|
|
|
|
{Package: "testprovider", Path: filepath.Join("..", "testprovider")},
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: validateOutputs(map[string]interface{}{
|
|
|
|
"xyz": "ABC",
|
|
|
|
"foo": float64(42),
|
|
|
|
}),
|
|
|
|
EditDirs: []integration.EditDir{
|
|
|
|
{
|
|
|
|
Dir: filepath.Join(d, "step2"),
|
|
|
|
Additive: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
// Expect the values to remain the same because the deployment ends before RegisterResourceOutputs is
|
|
|
|
// called for the stack.
|
|
|
|
ExtraRuntimeValidation: validateOutputs(map[string]interface{}{
|
|
|
|
"xyz": "ABC",
|
|
|
|
"foo": float64(42),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Dir: filepath.Join(d, "step3"),
|
|
|
|
Additive: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
// Expect the values to be updated.
|
|
|
|
ExtraRuntimeValidation: validateOutputs(map[string]interface{}{
|
|
|
|
"xyz": "DEF",
|
|
|
|
"foo": float64(1),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
// TestStackOutputsJSON ensures the CLI properly formats stack outputs as JSON when requested.
|
|
|
|
func TestStackOutputsJSON(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory(filepath.Join("stack_outputs", "nodejs"))
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
|
|
|
e.RunCommand("pulumi", "stack", "init", "stack-outs")
|
|
|
|
e.RunCommand("pulumi", "up", "--non-interactive", "--yes", "--skip-preview")
|
|
|
|
stdout, _ := e.RunCommand("pulumi", "stack", "output", "--json")
|
|
|
|
assert.Equal(t, `{
|
|
|
|
"foo": 42,
|
|
|
|
"xyz": "ABC"
|
|
|
|
}
|
|
|
|
`, stdout)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackOutputsDisplayed ensures that outputs are printed at the end of an update
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackOutputsDisplayed(t *testing.T) {
|
|
|
|
stdout := &bytes.Buffer{}
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("stack_outputs", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: false,
|
|
|
|
Verbose: true,
|
|
|
|
Stdout: stdout,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stdout.String()
|
|
|
|
|
|
|
|
// ensure we get the outputs info both for the normal update, and for the no-change update.
|
|
|
|
assert.Contains(t, output, "Outputs:\n foo: 42\n xyz: \"ABC\"\n\nResources:\n + 1 created")
|
|
|
|
assert.Contains(t, output, "Outputs:\n foo: 42\n xyz: \"ABC\"\n\nResources:\n 1 unchanged")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackOutputsSuppressed ensures that outputs whose values are intentionally suppresses don't show.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackOutputsSuppressed(t *testing.T) {
|
|
|
|
stdout := &bytes.Buffer{}
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("stack_outputs", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: false,
|
|
|
|
Verbose: true,
|
|
|
|
Stdout: stdout,
|
|
|
|
UpdateCommandlineFlags: []string{"--suppress-outputs"},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stdout.String()
|
|
|
|
assert.NotContains(t, output, "Outputs:\n foo: 42\n xyz: \"ABC\"\n")
|
|
|
|
assert.NotContains(t, output, "Outputs:\n foo: 42\n xyz: \"ABC\"\n")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackParenting tests out that stacks and components are parented correctly.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackParenting(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "stack_parenting",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure the checkpoint contains resources parented correctly. This should look like this:
|
|
|
|
//
|
|
|
|
// A F
|
|
|
|
// / \ \
|
|
|
|
// B C G
|
|
|
|
// / \
|
|
|
|
// D E
|
|
|
|
//
|
|
|
|
// with the caveat, of course, that A and F will share a common parent, the implicit stack.
|
|
|
|
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
if assert.Equal(t, 9, len(stackInfo.Deployment.Resources)) {
|
|
|
|
stackRes := stackInfo.Deployment.Resources[0]
|
|
|
|
assert.NotNil(t, stackRes)
|
|
|
|
assert.Equal(t, resource.RootStackType, stackRes.Type)
|
|
|
|
assert.Equal(t, "", string(stackRes.Parent))
|
|
|
|
|
|
|
|
urns := make(map[string]resource.URN)
|
|
|
|
for _, res := range stackInfo.Deployment.Resources[1:] {
|
|
|
|
assert.NotNil(t, res)
|
|
|
|
|
2023-11-28 16:43:48 +00:00
|
|
|
urns[res.URN.Name()] = res.URN
|
2020-09-02 16:11:15 +00:00
|
|
|
switch res.URN.Name() {
|
|
|
|
case "a", "f":
|
|
|
|
assert.NotEqual(t, "", res.Parent)
|
|
|
|
assert.Equal(t, stackRes.URN, res.Parent)
|
|
|
|
case "b", "c":
|
|
|
|
assert.Equal(t, urns["a"], res.Parent)
|
|
|
|
case "d", "e":
|
|
|
|
assert.Equal(t, urns["c"], res.Parent)
|
|
|
|
case "g":
|
|
|
|
assert.Equal(t, urns["f"], res.Parent)
|
|
|
|
case "default":
|
2022-11-07 13:15:36 +00:00
|
|
|
// Default providers should have the stack as a parent, but auto-parenting has been
|
|
|
|
// disabled so they won't have a parent for now.
|
|
|
|
assert.Equal(t, resource.URN(""), res.Parent)
|
2020-09-02 16:11:15 +00:00
|
|
|
default:
|
|
|
|
t.Fatalf("unexpected name %s", res.URN.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackBadParenting(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "stack_bad_parenting",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestStackDependencyGraph tests that the dependency graph of a stack is saved
|
|
|
|
// in the checkpoint file.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestStackDependencyGraph(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "stack_dependencies",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
latest := stackInfo.Deployment
|
|
|
|
assert.True(t, len(latest.Resources) >= 2)
|
|
|
|
sawFirst := false
|
|
|
|
sawSecond := false
|
|
|
|
for _, res := range latest.Resources {
|
|
|
|
urn := string(res.URN)
|
|
|
|
if strings.Contains(urn, "dynamic:Resource::first") {
|
|
|
|
// The first resource doesn't depend on anything.
|
|
|
|
assert.Equal(t, 0, len(res.Dependencies))
|
|
|
|
sawFirst = true
|
|
|
|
} else if strings.Contains(urn, "dynamic:Resource::second") {
|
|
|
|
// The second resource uses an Output property of the first resource, so it
|
|
|
|
// depends directly on first.
|
|
|
|
assert.Equal(t, 1, len(res.Dependencies))
|
|
|
|
assert.True(t, strings.Contains(string(res.Dependencies[0]), "dynamic:Resource::first"))
|
|
|
|
sawSecond = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.True(t, sawFirst && sawSecond)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests basic configuration from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestConfigBasicNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_basic", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{
|
|
|
|
"aConfigValue": "this value is a value",
|
|
|
|
},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"bEncryptedSecret": "this super secret is encrypted",
|
|
|
|
},
|
|
|
|
OrderedConfig: []integration.ConfigValue{
|
|
|
|
{Key: "outer.inner", Value: "value", Path: true},
|
|
|
|
{Key: "names[0]", Value: "a", Path: true},
|
|
|
|
{Key: "names[1]", Value: "b", Path: true},
|
|
|
|
{Key: "names[2]", Value: "c", Path: true},
|
|
|
|
{Key: "names[3]", Value: "super secret name", Path: true, Secret: true},
|
|
|
|
{Key: "servers[0].port", Value: "80", Path: true},
|
|
|
|
{Key: "servers[0].host", Value: "example", Path: true},
|
|
|
|
{Key: "a.b[0].c", Value: "true", Path: true},
|
|
|
|
{Key: "a.b[1].c", Value: "false", Path: true},
|
|
|
|
{Key: "tokens[0]", Value: "shh", Path: true, Secret: true},
|
|
|
|
{Key: "foo.bar", Value: "don't tell", Path: true, Secret: true},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-22 08:45:24 +00:00
|
|
|
// Tests configuration error from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-22 08:45:24 +00:00
|
|
|
func TestConfigMissingJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_missing", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
text1 := "Missing required configuration variable 'config_missing_js:notFound'"
|
|
|
|
text2 := "\tplease set a value using the command `pulumi config set --secret config_missing_js:notFound <value>`"
|
|
|
|
var found1, found2 bool
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil && strings.Contains(event.DiagnosticEvent.Message, text1) {
|
|
|
|
found1 = true
|
|
|
|
}
|
|
|
|
if event.DiagnosticEvent != nil && strings.Contains(event.DiagnosticEvent.Message, text2) {
|
|
|
|
found2 = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, found1, "expected error %q", text1)
|
|
|
|
assert.True(t, found2, "expected error %q", text2)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestConfigCaptureNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_capture_e2e", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{
|
|
|
|
"value": "it works",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-05-18 16:48:08 +00:00
|
|
|
// Tests that accessing config secrets using non-secret APIs results in warnings being logged.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-05-18 16:48:08 +00:00
|
|
|
func TestConfigSecretsWarnNodeJS(t *testing.T) {
|
2021-05-24 23:06:27 +00:00
|
|
|
// TODO[pulumi/pulumi#7127]: Re-enabled the warning.
|
|
|
|
t.Skip("Temporarily skipping test until we've re-enabled the warning - pulumi/pulumi#7127")
|
2021-05-18 16:48:08 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("config_secrets_warn", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{
|
|
|
|
"plainstr1": "1",
|
|
|
|
"plainstr2": "2",
|
|
|
|
"plainstr3": "3",
|
|
|
|
"plainstr4": "4",
|
|
|
|
"plainbool1": "true",
|
|
|
|
"plainbool2": "true",
|
|
|
|
"plainbool3": "true",
|
|
|
|
"plainbool4": "true",
|
|
|
|
"plainnum1": "1",
|
|
|
|
"plainnum2": "2",
|
|
|
|
"plainnum3": "3",
|
|
|
|
"plainnum4": "4",
|
|
|
|
"plainobj1": "{}",
|
|
|
|
"plainobj2": "{}",
|
|
|
|
"plainobj3": "{}",
|
|
|
|
"plainobj4": "{}",
|
|
|
|
},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"str1": "1",
|
|
|
|
"str2": "2",
|
|
|
|
"str3": "3",
|
|
|
|
"str4": "4",
|
|
|
|
"bool1": "true",
|
|
|
|
"bool2": "true",
|
|
|
|
"bool3": "true",
|
|
|
|
"bool4": "true",
|
|
|
|
"num1": "1",
|
|
|
|
"num2": "2",
|
|
|
|
"num3": "3",
|
|
|
|
"num4": "4",
|
|
|
|
"obj1": "{}",
|
|
|
|
"obj2": "{}",
|
|
|
|
"obj3": "{}",
|
|
|
|
"obj4": "{}",
|
|
|
|
},
|
|
|
|
OrderedConfig: []integration.ConfigValue{
|
|
|
|
{Key: "parent1.foo", Value: "plain1", Path: true},
|
|
|
|
{Key: "parent1.bar", Value: "secret1", Path: true, Secret: true},
|
|
|
|
{Key: "parent2.foo", Value: "plain2", Path: true},
|
|
|
|
{Key: "parent2.bar", Value: "secret2", Path: true, Secret: true},
|
|
|
|
{Key: "names1[0]", Value: "plain1", Path: true},
|
|
|
|
{Key: "names1[1]", Value: "secret1", Path: true, Secret: true},
|
|
|
|
{Key: "names2[0]", Value: "plain2", Path: true},
|
|
|
|
{Key: "names2[1]", Value: "secret2", Path: true, Secret: true},
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
//nolint:lll
|
|
|
|
expectedWarnings := []string{
|
|
|
|
"Configuration 'config_secrets_node:str1' value is a secret; use `getSecret` instead of `get`",
|
|
|
|
"Configuration 'config_secrets_node:str2' value is a secret; use `requireSecret` instead of `require`",
|
|
|
|
"Configuration 'config_secrets_node:bool1' value is a secret; use `getSecretBoolean` instead of `getBoolean`",
|
|
|
|
"Configuration 'config_secrets_node:bool2' value is a secret; use `requireSecretBoolean` instead of `requireBoolean`",
|
|
|
|
"Configuration 'config_secrets_node:num1' value is a secret; use `getSecretNumber` instead of `getNumber`",
|
|
|
|
"Configuration 'config_secrets_node:num2' value is a secret; use `requireSecretNumber` instead of `requireNumber`",
|
|
|
|
"Configuration 'config_secrets_node:obj1' value is a secret; use `getSecretObject` instead of `getObject`",
|
|
|
|
"Configuration 'config_secrets_node:obj2' value is a secret; use `requireSecretObject` instead of `requireObject`",
|
|
|
|
"Configuration 'config_secrets_node:parent1' value is a secret; use `getSecretObject` instead of `getObject`",
|
|
|
|
"Configuration 'config_secrets_node:parent2' value is a secret; use `requireSecretObject` instead of `requireObject`",
|
|
|
|
"Configuration 'config_secrets_node:names1' value is a secret; use `getSecretObject` instead of `getObject`",
|
|
|
|
"Configuration 'config_secrets_node:names2' value is a secret; use `requireSecretObject` instead of `requireObject`",
|
|
|
|
}
|
|
|
|
for _, warning := range expectedWarnings {
|
|
|
|
var found bool
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil && event.DiagnosticEvent.Severity == "warning" &&
|
|
|
|
strings.Contains(event.DiagnosticEvent.Message, warning) {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, found, "expected warning %q", warning)
|
|
|
|
}
|
|
|
|
|
|
|
|
// These keys should not be in any warning messages.
|
|
|
|
unexpectedWarnings := []string{
|
|
|
|
"plainstr1",
|
|
|
|
"plainstr2",
|
|
|
|
"plainstr3",
|
|
|
|
"plainstr4",
|
|
|
|
"plainbool1",
|
|
|
|
"plainbool2",
|
|
|
|
"plainbool3",
|
|
|
|
"plainbool4",
|
|
|
|
"plainnum1",
|
|
|
|
"plainnum2",
|
|
|
|
"plainnum3",
|
|
|
|
"plainnum4",
|
|
|
|
"plainobj1",
|
|
|
|
"plainobj2",
|
|
|
|
"plainobj3",
|
|
|
|
"plainobj4",
|
|
|
|
"str3",
|
|
|
|
"str4",
|
|
|
|
"bool3",
|
|
|
|
"bool4",
|
|
|
|
"num3",
|
|
|
|
"num4",
|
|
|
|
"obj3",
|
|
|
|
"obj4",
|
|
|
|
}
|
|
|
|
for _, warning := range unexpectedWarnings {
|
|
|
|
for _, event := range stackInfo.Events {
|
|
|
|
if event.DiagnosticEvent != nil {
|
|
|
|
assert.NotContains(t, event.DiagnosticEvent.Message, warning)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestInvalidVersionInPackageJson(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("invalid_package_json"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests an explicit provider instance.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestExplicitProvider(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "explicit_provider",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
latest := stackInfo.Deployment
|
|
|
|
|
|
|
|
// Expect one stack resource, two provider resources, and two custom resources.
|
|
|
|
assert.True(t, len(latest.Resources) == 5)
|
|
|
|
|
|
|
|
var defaultProvider *apitype.ResourceV3
|
|
|
|
var explicitProvider *apitype.ResourceV3
|
|
|
|
for _, res := range latest.Resources {
|
|
|
|
urn := res.URN
|
|
|
|
switch urn.Name() {
|
|
|
|
case "default":
|
|
|
|
assert.True(t, providers.IsProviderType(res.Type))
|
|
|
|
assert.Nil(t, defaultProvider)
|
|
|
|
prov := res
|
|
|
|
defaultProvider = &prov
|
|
|
|
|
|
|
|
case "p":
|
|
|
|
assert.True(t, providers.IsProviderType(res.Type))
|
|
|
|
assert.Nil(t, explicitProvider)
|
|
|
|
prov := res
|
|
|
|
explicitProvider = &prov
|
|
|
|
|
|
|
|
case "a":
|
|
|
|
prov, err := providers.ParseReference(res.Provider)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, defaultProvider)
|
|
|
|
defaultRef, err := providers.NewReference(defaultProvider.URN, defaultProvider.ID)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, defaultRef.String(), prov.String())
|
|
|
|
|
|
|
|
case "b":
|
|
|
|
prov, err := providers.ParseReference(res.Provider)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, explicitProvider)
|
|
|
|
explicitRef, err := providers.NewReference(explicitProvider.URN, explicitProvider.ID)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, explicitRef.String(), prov.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.NotNil(t, defaultProvider)
|
|
|
|
assert.NotNil(t, explicitProvider)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests that reads of unknown IDs do not fail.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestGetCreated(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "get_created",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestProviderSecretConfig that a first class provider can be created when it has secrets as part of its config.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestProviderSecretConfig(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: "provider_secret_config",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-10-01 21:57:51 +00:00
|
|
|
func TestResourceWithSecretSerializationNodejs(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("secret_outputs", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// The program exports three resources:
|
|
|
|
// 1. One named `withSecret` who's prefix property should be secret, specified via `pulumi.secret()`.
|
|
|
|
// 2. One named `withSecretAdditional` who's prefix property should be a secret, specified via
|
|
|
|
// additionalSecretOutputs.
|
|
|
|
// 3. One named `withoutSecret` which should not be a secret.
|
|
|
|
// We serialize both of the these as POJO objects, so they appear as maps in the output.
|
|
|
|
withSecretProps, ok := stackInfo.Outputs["withSecret"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
withSecretAdditionalProps, ok := stackInfo.Outputs["withSecretAdditional"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
withoutSecretProps, ok := stackInfo.Outputs["withoutSecret"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "POJO output was not serialized as a map")
|
|
|
|
|
|
|
|
// The secret prop should have been serialized as a secret
|
|
|
|
secretPropValue, ok := withSecretProps["prefix"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "secret output was not serialized as a secret")
|
|
|
|
assert.Equal(t, resource.SecretSig, secretPropValue[resource.SigKey].(string))
|
|
|
|
|
|
|
|
// The other secret prop should have been serialized as a secret
|
|
|
|
secretAdditionalPropValue, ok := withSecretAdditionalProps["prefix"].(map[string]interface{})
|
|
|
|
assert.Truef(t, ok, "secret output was not serialized as a secret")
|
|
|
|
assert.Equal(t, resource.SecretSig, secretAdditionalPropValue[resource.SigKey].(string))
|
|
|
|
|
|
|
|
// And here, the prop was not set, it should just be a string value
|
|
|
|
_, isString := withoutSecretProps["prefix"].(string)
|
|
|
|
assert.Truef(t, isString, "non-secret output was not a string")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
//nolint:paralleltest // mutates environment variables
|
2021-05-14 22:00:09 +00:00
|
|
|
func TestPasswordlessPassphraseSecretsProvider(t *testing.T) {
|
|
|
|
testOptions := integration.ProgramTestOptions{
|
|
|
|
Dir: "cloud_secrets_provider",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
2023-11-28 16:43:48 +00:00
|
|
|
SecretsProvider: "passphrase",
|
2021-05-14 22:00:09 +00:00
|
|
|
Env: []string{"PULUMI_CONFIG_PASSPHRASE=\"\""},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"mysecret": "THISISASECRET",
|
|
|
|
},
|
2022-09-14 21:02:36 +00:00
|
|
|
CloudURL: integration.MakeTempBackend(t),
|
2022-03-04 08:17:41 +00:00
|
|
|
NoParallel: true, // mutates environment variables
|
2021-05-14 22:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
workingTestOptions := testOptions.With(integration.ProgramTestOptions{
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
2022-07-24 09:41:44 +00:00
|
|
|
t.Setenv("PULUMI_CONFIG_PASSPHRASE", "password")
|
2021-05-14 22:00:09 +00:00
|
|
|
secretsProvider := stackInfo.Deployment.SecretsProviders
|
|
|
|
assert.NotNil(t, secretsProvider)
|
|
|
|
assert.Equal(t, secretsProvider.Type, "passphrase")
|
|
|
|
|
2023-01-03 13:37:06 +00:00
|
|
|
_, err := passphrase.NewPromptingPassphraseSecretsManagerFromState(secretsProvider.State)
|
2021-05-14 22:00:09 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
out, ok := stackInfo.Outputs["out"].(map[string]interface{})
|
|
|
|
assert.True(t, ok)
|
|
|
|
|
|
|
|
_, ok = out["ciphertext"]
|
|
|
|
assert.True(t, ok)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
brokenTestOptions := testOptions.With(integration.ProgramTestOptions{
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
secretsProvider := stackInfo.Deployment.SecretsProviders
|
|
|
|
assert.NotNil(t, secretsProvider)
|
|
|
|
assert.Equal(t, secretsProvider.Type, "passphrase")
|
|
|
|
|
2023-01-03 13:37:06 +00:00
|
|
|
_, err := passphrase.NewPromptingPassphraseSecretsManagerFromState(secretsProvider.State)
|
2021-05-14 22:00:09 +00:00
|
|
|
assert.Error(t, err)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("works-when-passphrase-set", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &workingTestOptions)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error-when-passphrase-not-set", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &brokenTestOptions)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestCloudSecretProvider(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-09-02 16:11:15 +00:00
|
|
|
awsKmsKeyAlias := os.Getenv("PULUMI_TEST_KMS_KEY_ALIAS")
|
|
|
|
if awsKmsKeyAlias == "" {
|
|
|
|
t.Skipf("Skipping: PULUMI_TEST_KMS_KEY_ALIAS is not set")
|
|
|
|
}
|
|
|
|
|
|
|
|
azureKeyVault := os.Getenv("PULUMI_TEST_AZURE_KEY")
|
|
|
|
if azureKeyVault == "" {
|
|
|
|
t.Skipf("Skipping: PULUMI_TEST_AZURE_KEY is not set")
|
|
|
|
}
|
|
|
|
|
|
|
|
gcpKmsKey := os.Getenv("PULUMI_TEST_GCP_KEY")
|
2024-07-16 14:07:02 +00:00
|
|
|
if gcpKmsKey == "" {
|
2020-09-02 16:11:15 +00:00
|
|
|
t.Skipf("Skipping: PULUMI_TEST_GCP_KEY is not set")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generic test options for all providers
|
|
|
|
testOptions := integration.ProgramTestOptions{
|
|
|
|
Dir: "cloud_secrets_provider",
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
2024-04-19 06:20:33 +00:00
|
|
|
SecretsProvider: "awskms://alias/" + awsKmsKeyAlias,
|
2020-09-02 16:11:15 +00:00
|
|
|
Secrets: map[string]string{
|
|
|
|
"mysecret": "THISISASECRET",
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
secretsProvider := stackInfo.Deployment.SecretsProviders
|
|
|
|
assert.NotNil(t, secretsProvider)
|
|
|
|
assert.Equal(t, secretsProvider.Type, "cloud")
|
|
|
|
|
|
|
|
_, err := cloud.NewCloudSecretsManagerFromState(secretsProvider.State)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
out, ok := stackInfo.Outputs["out"].(map[string]interface{})
|
|
|
|
assert.True(t, ok)
|
|
|
|
|
|
|
|
_, ok = out["ciphertext"]
|
|
|
|
assert.True(t, ok)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
localTestOptions := testOptions.With(integration.ProgramTestOptions{
|
2022-09-14 21:02:36 +00:00
|
|
|
CloudURL: integration.MakeTempBackend(t),
|
2020-09-02 16:11:15 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
azureTestOptions := testOptions.With(integration.ProgramTestOptions{
|
2024-04-19 06:20:33 +00:00
|
|
|
SecretsProvider: "azurekeyvault://" + azureKeyVault,
|
2020-09-02 16:11:15 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
gcpTestOptions := testOptions.With(integration.ProgramTestOptions{
|
2024-04-19 06:20:33 +00:00
|
|
|
SecretsProvider: "gcpkms://projects/" + gcpKmsKey,
|
2020-09-02 16:11:15 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// Run with default Pulumi service backend
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
t.Run("service", func(t *testing.T) { integration.ProgramTest(t, &testOptions) })
|
2020-09-02 16:11:15 +00:00
|
|
|
|
|
|
|
// Check Azure secrets provider
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
t.Run("azure", func(t *testing.T) { integration.ProgramTest(t, &azureTestOptions) })
|
|
|
|
|
|
|
|
// Check gcloud secrets provider
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
t.Run("gcp", func(t *testing.T) { integration.ProgramTest(t, &gcpTestOptions) })
|
|
|
|
|
2024-01-30 15:53:10 +00:00
|
|
|
// Also run with local diy backend
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
t.Run("local", func(t *testing.T) { integration.ProgramTest(t, &localTestOptions) })
|
|
|
|
}
|
|
|
|
|
2020-10-22 17:53:29 +00:00
|
|
|
// Tests a resource with a large (>4mb) string prop in Node.js
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-09-02 16:11:15 +00:00
|
|
|
func TestLargeResourceNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("large_resource", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
})
|
|
|
|
}
|
Initial support for remote component construction. (#5280)
These changes add initial support for the construction of remote
components. For now, this support is limited to the NodeJS SDK;
follow-up changes will implement support for the other SDKs.
Remote components are component resources that are constructed and
managed by plugins rather than by Pulumi programs. In this sense, they
are a bit like cloud resources, and are supported by the same
distribution and plugin loading mechanisms and described by the same
schema system.
The construction of a remote component is initiated by a
`RegisterResourceRequest` with the new `remote` field set to `true`.
When the resource monitor receives such a request, it loads the plugin
that implements the component resource and calls the `Construct`
method added to the resource provider interface as part of these
changes. This method accepts the information necessary to construct the
component and its children: the component's name, type, resource
options, inputs, and input dependencies. It is responsible for
dispatching to the appropriate component factory to create the
component, then returning its URN, resolved output properties, and
output property dependencies. The dependency information is necessary to
support features such as delete-before-replace, which rely on precise
dependency information for custom resources.
These changes also add initial support for more conveniently
implementing resource providers in NodeJS. The interface used to
implement such a provider is similar to the dynamic provider interface
(and may be unified with that interface in the future).
An example of a NodeJS program constructing a remote component resource
also implemented in NodeJS can be found in
`tests/construct_component/nodejs`.
This is the core of #2430.
2020-09-08 02:33:55 +00:00
|
|
|
|
2020-10-22 17:53:29 +00:00
|
|
|
// Tests enum outputs
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-10-22 17:53:29 +00:00
|
|
|
func TestEnumOutputNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("enums", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stack.Outputs)
|
|
|
|
assert.Equal(t, "Burgundy", stack.Outputs["myTreeType"])
|
|
|
|
assert.Equal(t, "Pulumi Planters Inc.foo", stack.Outputs["myTreeFarmChanged"])
|
|
|
|
assert.Equal(t, "My Burgundy Rubber tree is from Pulumi Planters Inc.", stack.Outputs["mySentence"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-05 18:11:27 +00:00
|
|
|
// Test remote component construction with a child resource that takes a long time to be created, ensuring it's created.
|
|
|
|
func TestConstructSlowNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := testComponentSlowLocalProvider(t)
|
2021-04-05 18:11:27 +00:00
|
|
|
|
|
|
|
var opts *integration.ProgramTestOptions
|
2022-09-14 02:24:22 +00:00
|
|
|
|
|
|
|
testDir := "construct_component_slow"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-04-05 18:11:27 +00:00
|
|
|
opts = &integration.ProgramTestOptions{
|
2022-09-20 15:16:25 +00:00
|
|
|
Dir: filepath.Join(testDir, "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
|
|
|
Quick: true,
|
2023-12-02 17:16:09 +00:00
|
|
|
NoParallel: true,
|
2021-04-05 18:11:27 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
if assert.Equal(t, 5, len(stackInfo.Deployment.Resources)) {
|
|
|
|
stackRes := stackInfo.Deployment.Resources[0]
|
|
|
|
assert.NotNil(t, stackRes)
|
|
|
|
assert.Equal(t, resource.RootStackType, stackRes.Type)
|
|
|
|
assert.Equal(t, "", string(stackRes.Parent))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
integration.ProgramTest(t, opts)
|
|
|
|
}
|
|
|
|
|
2021-04-09 21:36:22 +00:00
|
|
|
// Test remote component construction with prompt inputs.
|
|
|
|
func TestConstructPlainNode(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-09-14 02:24:22 +00:00
|
|
|
testDir := "construct_component_plain"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-04-18 16:18:25 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
expectedResourceCount int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
expectedResourceCount: 9,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
expectedResourceCount: 9,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
expectedResourceCount: 8, // One less because no dynamic provider.
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-04-18 16:18:25 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-04-18 16:18:25 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2023-03-03 16:36:39 +00:00
|
|
|
localProviders := []integration.LocalDependency{
|
|
|
|
{Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir)},
|
|
|
|
}
|
2021-04-18 16:18:25 +00:00
|
|
|
integration.ProgramTest(t,
|
2022-09-20 15:16:25 +00:00
|
|
|
optsForConstructPlainNode(t, test.expectedResourceCount, localProviders))
|
2021-04-18 16:18:25 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-28 16:43:48 +00:00
|
|
|
func optsForConstructPlainNode(
|
|
|
|
t *testing.T, expectedResourceCount int, localProviders []integration.LocalDependency,
|
|
|
|
) *integration.ProgramTestOptions {
|
2021-04-18 16:18:25 +00:00
|
|
|
return &integration.ProgramTestOptions{
|
2022-09-20 15:16:25 +00:00
|
|
|
Dir: filepath.Join("construct_component_plain", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
LocalProviders: localProviders,
|
|
|
|
Quick: true,
|
2021-04-09 21:36:22 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
2021-04-18 16:18:25 +00:00
|
|
|
assert.Equal(t, expectedResourceCount, len(stackInfo.Deployment.Resources))
|
2021-04-09 21:36:22 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 16:51:41 +00:00
|
|
|
// Test remote component inputs properly handle unknowns.
|
|
|
|
func TestConstructUnknownNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-06-21 16:51:41 +00:00
|
|
|
testConstructUnknown(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
|
|
|
|
2021-07-07 23:03:56 +00:00
|
|
|
// Test methods on remote components.
|
|
|
|
func TestConstructMethodsNode(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-09-14 02:24:22 +00:00
|
|
|
testDir := "construct_component_methods"
|
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-07-07 23:03:56 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
},
|
2021-07-19 21:58:55 +00:00
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
},
|
2021-07-09 01:39:37 +00:00
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
},
|
2021-07-07 23:03:56 +00:00
|
|
|
}
|
2023-12-02 17:16:09 +00:00
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-07-07 23:03:56 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-07-07 23:03:56 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := integration.LocalDependency{
|
2022-09-22 14:10:40 +00:00
|
|
|
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
|
2022-09-20 15:16:25 +00:00
|
|
|
}
|
2021-07-07 23:03:56 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2022-09-20 16:12:43 +00:00
|
|
|
Dir: filepath.Join(testDir, "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
2022-09-20 16:12:43 +00:00
|
|
|
Quick: true,
|
2021-07-07 23:03:56 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, "Hello World, Alice!", stackInfo.Outputs["message"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-29 18:11:52 +00:00
|
|
|
func TestConstructMethodsUnknownNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-07-29 18:11:52 +00:00
|
|
|
testConstructMethodsUnknown(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
|
|
|
|
2021-11-15 19:17:53 +00:00
|
|
|
func TestConstructMethodsResourcesNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-15 19:17:53 +00:00
|
|
|
testConstructMethodsResources(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
|
|
|
|
2021-11-16 16:58:46 +00:00
|
|
|
func TestConstructMethodsErrorsNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-16 16:58:46 +00:00
|
|
|
testConstructMethodsErrors(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
|
|
|
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
func TestConstructMethodsProviderNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
[sdk/nodejs] Fix provider for resource methods (#13796)
The `Resource` class in the Node.js SDK has the following internal
property:
```typescript
/** @internal */
readonly __prov?: ProviderResource;
```
When a resource is created, the provider specified for the resource is
stored in this property. If it is set, it is passed along in the `Call`
request when a method is called on the resource.
Prior to #13282, the property was only set for custom resources in
`Resource`'s constructor:
```typescript
this.__prov = custom ? opts.provider : undefined;
```
With #13282, it was changed to also store the value for remote
components:
```diff
- this.__prov = custom ? opts.provider : undefined;
+ this.__prov = custom || remote ? opts.provider : undefined;
```
This regressed the behavior when calling a method on a remote component
that had an explicit provider that wasn't the component provider, but
some other provider (e.g. AWS provider) specified as:
```typescript
const component = new MyRemoteComponent("comp", {
}, { provider: awsProvider });
```
The `awsProvider` was being stored in `Resource.__prov`, and when making
the method call on the resource, it would try to invoke `Call` on the
AWS provider, rather than calling the remote component provider's
`Call`, which resulted in an error.
Note that specifying the AWS provider using the more verbose `providers:
[awsProvider]` works around the issue.
The fix is to only set `__prov` if the provider's package is the same as
the resource's package. Otherwise, don't set it, because the user is
specifying a provider with the `provider: awsProvider` syntax as
shorthand for `providers: [awsProvider]`.
Fixes #13777
2023-08-30 14:49:53 +00:00
|
|
|
testConstructMethodsProvider(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
func TestConstructProviderNode(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
const testDir = "construct_component_provider"
|
2022-09-14 02:24:22 +00:00
|
|
|
runComponentSetup(t, testDir)
|
|
|
|
|
2021-07-23 21:10:06 +00:00
|
|
|
tests := []struct {
|
|
|
|
componentDir string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-python",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
componentDir: "testcomponent-go",
|
|
|
|
},
|
|
|
|
}
|
2023-12-02 17:16:09 +00:00
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-07-23 21:10:06 +00:00
|
|
|
for _, test := range tests {
|
2022-03-04 08:17:41 +00:00
|
|
|
test := test
|
2021-07-23 21:10:06 +00:00
|
|
|
t.Run(test.componentDir, func(t *testing.T) {
|
2022-09-20 15:16:25 +00:00
|
|
|
localProvider := integration.LocalDependency{
|
2022-09-22 14:10:40 +00:00
|
|
|
Package: "testcomponent", Path: filepath.Join(testDir, test.componentDir),
|
2022-09-20 15:16:25 +00:00
|
|
|
}
|
2021-07-23 21:10:06 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2022-09-20 16:12:43 +00:00
|
|
|
Dir: filepath.Join(testDir, "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
2022-09-20 15:16:25 +00:00
|
|
|
LocalProviders: []integration.LocalDependency{localProvider},
|
2022-09-20 16:12:43 +00:00
|
|
|
Quick: true,
|
2021-07-23 21:10:06 +00:00
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, "hello world", stackInfo.Outputs["message"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2020-12-01 18:58:15 +00:00
|
|
|
func TestGetResourceNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("get_resource", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
AllowEmptyPreviewChanges: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stack.Outputs)
|
|
|
|
assert.Equal(t, "foo", stack.Outputs["foo"])
|
2022-07-26 18:04:13 +00:00
|
|
|
|
|
|
|
out, ok := stack.Outputs["secret"].(map[string]interface{})
|
|
|
|
assert.True(t, ok)
|
|
|
|
|
|
|
|
_, ok = out["ciphertext"]
|
|
|
|
assert.True(t, ok)
|
2020-12-01 18:58:15 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2021-05-19 14:11:18 +00:00
|
|
|
|
|
|
|
func TestComponentProviderSchemaNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-05-19 14:11:18 +00:00
|
|
|
path := filepath.Join("component_provider_schema", "testcomponent", "pulumi-resource-testcomponent")
|
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
path += ".cmd"
|
|
|
|
}
|
|
|
|
testComponentProviderSchema(t, path)
|
|
|
|
}
|
2021-06-26 01:41:54 +00:00
|
|
|
|
|
|
|
// Test throwing an error within an apply in a remote component written in nodejs.
|
|
|
|
// The provider should return the error and shutdown gracefully rather than hanging.
|
|
|
|
func TestConstructNodeErrorApply(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2021-06-26 01:41:54 +00:00
|
|
|
dir := "construct_component_error_apply"
|
|
|
|
componentDir := "testcomponent"
|
|
|
|
|
2022-09-14 02:24:22 +00:00
|
|
|
runComponentSetup(t, dir)
|
|
|
|
|
2021-06-26 01:41:54 +00:00
|
|
|
stderr := &bytes.Buffer{}
|
|
|
|
expectedError := "intentional error from within an apply"
|
|
|
|
|
|
|
|
opts := &integration.ProgramTestOptions{
|
2022-09-20 16:12:43 +00:00
|
|
|
Dir: filepath.Join(dir, "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
LocalProviders: []integration.LocalDependency{
|
2022-09-22 14:10:40 +00:00
|
|
|
{Package: "testcomponent", Path: filepath.Join(dir, componentDir)},
|
2022-09-20 16:12:43 +00:00
|
|
|
},
|
2021-06-26 01:41:54 +00:00
|
|
|
Quick: true,
|
|
|
|
Stderr: stderr,
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
output := stderr.String()
|
|
|
|
assert.Contains(t, output, expectedError)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run(componentDir, func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, opts)
|
|
|
|
})
|
|
|
|
}
|
2021-08-16 01:58:43 +00:00
|
|
|
|
2022-08-24 02:33:29 +00:00
|
|
|
// Test to ensure that internal stacks are hidden
|
|
|
|
func TestNodejsStackTruncate(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-08-24 02:33:29 +00:00
|
|
|
cases := []string{
|
|
|
|
"syntax-error",
|
|
|
|
"ts-error",
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-08-24 02:33:29 +00:00
|
|
|
for _, name := range cases {
|
|
|
|
// Test the program.
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "omit-stacktrace", name),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
// This test should fail because it raises an exception
|
|
|
|
ExpectFailure: true,
|
|
|
|
// We need to validate that the failure has a truncated stack trace
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure that we have a non-empty list of events.
|
|
|
|
assert.NotEmpty(t, stackInfo.Events)
|
|
|
|
|
|
|
|
const stacktraceLinePrefix = " at "
|
|
|
|
|
|
|
|
// get last DiagnosticEvent containing python stack trace
|
|
|
|
stackTraceMessage := ""
|
|
|
|
for _, e := range stackInfo.Events {
|
|
|
|
if e.DiagnosticEvent == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
msg := e.DiagnosticEvent.Message
|
|
|
|
if !strings.Contains(msg, stacktraceLinePrefix) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
stackTraceMessage = msg
|
|
|
|
}
|
|
|
|
assert.Equal(t, "", stackTraceMessage)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-16 01:58:43 +00:00
|
|
|
// Test targeting `es2016` in `tsconfig.json` works.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2021-08-16 01:58:43 +00:00
|
|
|
func TestCompilerOptionsNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "compiler_options"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
2021-08-27 17:39:49 +00:00
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-01-05 02:54:38 +00:00
|
|
|
func TestESMJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-js"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-01-05 02:54:38 +00:00
|
|
|
func TestESMJSMain(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-js-main"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-01-05 02:54:38 +00:00
|
|
|
func TestESMTS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-ts"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-24 00:20:43 +00:00
|
|
|
func TestTSWithPackageJsonInParentDir(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "ts-with-package-json-in-parent-dir"),
|
|
|
|
RelativeWorkDir: filepath.Join("myprogram"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-24 00:20:43 +00:00
|
|
|
func TestESMWithPackageJsonInParentDir(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-with-package-json-in-parent-dir"),
|
|
|
|
RelativeWorkDir: filepath.Join("myprogram"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-24 00:20:43 +00:00
|
|
|
func TestESMWithoutPackageJsonInParentDir(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-package-json-in-parent-dir-without-main"),
|
|
|
|
RelativeWorkDir: filepath.Join("myprogram"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-24 00:20:43 +00:00
|
|
|
func TestPackageJsonInParentDirWithoutMain(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "package-json-in-parent-dir-without-main"),
|
|
|
|
RelativeWorkDir: filepath.Join("myprogram"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-24 00:20:43 +00:00
|
|
|
func TestESMTSNestedSrc(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-ts-nested-src"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
Config: map[string]string{
|
|
|
|
"test": "hello world",
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Len(t, stack.Outputs, 1)
|
|
|
|
test, ok := stack.Outputs["test"]
|
|
|
|
assert.True(t, ok)
|
|
|
|
assert.Equal(t, "hello world", test)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
[sdk/nodejs] Support top-level default exports in ESM (#8766)
In #7764 and #8655 we added support for ESM entrypoints. However, ESM "default exports" were handled just as "normal" in Node.js dynamic import of ESM - as a `default` proeprty in the export object.
This is not a particularly useful behaviour for Pulumi program entry points, and doesn't quite match some of the special logic we apply to non-object exports in CommonJS modules (invoking exported functions, and then awaiting exports promises).
Instead, this change adds support for default exports, treating the default export (if present) as the full returned export value.
It is for now an error to have both a default export and named exports, since it is unclear what this should mean. In the future, we could potentially relax this and define how these two sets of exports are merged.
This is technically a breaking change from the support added in the recent releases, but only in a narrow case, and in that case the Pulumi stack exports were almost certainly not what the user wanted.
Fixes #8725, which includes a motivating example where this is ~necessary.
2022-01-23 02:33:28 +00:00
|
|
|
func TestESMTSDefaultExport(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-ts-default-export"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Len(t, stack.Outputs, 1)
|
|
|
|
helloWorld, ok := stack.Outputs["helloWorld"]
|
|
|
|
assert.True(t, ok)
|
|
|
|
assert.Equal(t, helloWorld, 123.0)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-01-05 02:54:38 +00:00
|
|
|
func TestESMTSSpecifierResolutionNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-ts-specifier-resolution-node"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:16:09 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-01-05 02:54:38 +00:00
|
|
|
func TestESMTSCompiled(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "esm-ts-compiled"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
RunBuild: true,
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-06-11 11:36:34 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestMainOverridesPackageJSON(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "main-overrides-package-json"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.NotNil(t, stack.Outputs)
|
|
|
|
assert.Equal(t, "This is the entrypoint from Pulumi.yaml", stack.Outputs["text"])
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-16 08:25:12 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestNpmWorkspace(t *testing.T) {
|
|
|
|
preparePropject := func(projinfo *engine.Projinfo) error {
|
|
|
|
// The default nodejs prepare uses yarn to link dependencies.
|
|
|
|
// For this test we don't want to test the current SDK, instead we
|
|
|
|
// want to test `pulumi install` and ensure that it works with npm
|
|
|
|
// workspaces.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "npm-and-yarn-workspaces"),
|
|
|
|
Quick: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
PrepareProject: preparePropject,
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
pt.TestFinished = true
|
|
|
|
pt.TestCleanUp()
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
require.NoError(t, pt.RunPulumiCommand("install"), "install")
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update")
|
|
|
|
require.NoError(t, pt.TestLifeCycleDestroy(), "destroy")
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // mutates environment variables
|
|
|
|
func TestYarnWorkspace(t *testing.T) {
|
|
|
|
t.Setenv("PULUMI_PREFER_YARN", "true")
|
|
|
|
preparePropject := func(projinfo *engine.Projinfo) error {
|
|
|
|
// The default nodejs prepare uses yarn to link dependencies.
|
|
|
|
// For this test we don't want to test the current SDK, instead we
|
|
|
|
// want to test `pulumi install` and ensure that it works with yarn
|
|
|
|
// workspaces.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "npm-and-yarn-workspaces"),
|
|
|
|
Quick: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
PrepareProject: preparePropject,
|
|
|
|
NoParallel: true, // mutates environment variables
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
pt.TestFinished = true
|
|
|
|
pt.TestCleanUp()
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
require.NoError(t, pt.RunPulumiCommand("install"), "install")
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update")
|
|
|
|
require.NoError(t, pt.TestLifeCycleDestroy(), "destroy")
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // mutates environment variables
|
|
|
|
func TestYarnWorkspaceNoHoist(t *testing.T) {
|
|
|
|
t.Setenv("PULUMI_PREFER_YARN", "true")
|
|
|
|
preparePropject := func(projinfo *engine.Projinfo) error {
|
|
|
|
// The default nodejs prepare uses yarn to link dependencies.
|
|
|
|
// For this test we don't want to test the current SDK, instead we
|
|
|
|
// want to test `pulumi install` and ensure that it works with yarn
|
|
|
|
// workspaces.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "yarn-workspaces-nohoist"),
|
|
|
|
Quick: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
PrepareProject: preparePropject,
|
|
|
|
NoParallel: true, // mutates environment variables
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
pt.TestFinished = true
|
|
|
|
pt.TestCleanUp()
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
require.NoError(t, pt.RunPulumiCommand("install"), "install")
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update")
|
|
|
|
require.NoError(t, pt.TestLifeCycleDestroy(), "destroy")
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestNestedPackageJSON(t *testing.T) {
|
|
|
|
preparePropject := func(projinfo *engine.Projinfo) error {
|
|
|
|
// The default nodejs prepare uses yarn to link dependencies.
|
|
|
|
// For this test we don't want to test the current SDK, instead we
|
|
|
|
// want to test `pulumi install` and ensure that it works with npm
|
|
|
|
// workspaces.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
dir := filepath.Join("nodejs", "npm-and-yarn-not-a-workspace")
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: dir,
|
|
|
|
Quick: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
PrepareProject: preparePropject,
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
pt.TestFinished = true
|
|
|
|
pt.TestCleanUp()
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
require.NoError(t, pt.RunPulumiCommand("install"), "install")
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update")
|
|
|
|
|
2024-06-18 13:37:06 +00:00
|
|
|
// There is no node_modules directory in the test directory (parent of program dir)
|
|
|
|
_, err := os.Stat(filepath.Join(pt.GetTmpDir(), "node_modules"))
|
2024-02-16 08:25:12 +00:00
|
|
|
require.True(t, os.IsNotExist(err))
|
|
|
|
|
2024-06-18 13:37:06 +00:00
|
|
|
// node_modules was created inside the program directory
|
|
|
|
_, err = os.Stat(filepath.Join(pt.GetTmpDir(), "infra", "node_modules"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCycleDestroy(), "destroy")
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestPnpmWorkspace(t *testing.T) {
|
|
|
|
preparePropject := func(projinfo *engine.Projinfo) error {
|
|
|
|
// The default nodejs prepare uses yarn to link dependencies.
|
|
|
|
// For this test we don't want to test the current SDK, instead we
|
|
|
|
// want to test `pulumi install` and ensure that it works with yarn
|
|
|
|
// workspaces.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pt := integration.ProgramTestManualLifeCycle(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "pnpm-workspace"),
|
|
|
|
Quick: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
PrepareProject: preparePropject,
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
pt.TestFinished = true
|
|
|
|
pt.TestCleanUp()
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCyclePrepare(), "prepare")
|
|
|
|
require.NoError(t, pt.RunPulumiCommand("install"), "install")
|
|
|
|
|
|
|
|
_, err := os.Stat(filepath.Join(pt.GetTmpDir(), "node_modules", ".pnpm"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.NoError(t, pt.TestLifeCycleInitialize(), "initialize")
|
|
|
|
require.NoError(t, pt.TestPreviewUpdateAndEdits(), "update")
|
2024-02-16 08:25:12 +00:00
|
|
|
require.NoError(t, pt.TestLifeCycleDestroy(), "destroy")
|
|
|
|
}
|
|
|
|
|
2024-02-26 18:40:28 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePaths(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-08 16:16:47 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePathsTSC(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths-tsc"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
RunBuild: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-22 11:41:37 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePathsNested(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths-nested"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
RelativeWorkDir: "nested",
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePathsWorkspace(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths-workspaces"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-08 16:16:47 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePathsWorkspaceTSC(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths-workspaces-tsc"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
RunBuild: true,
|
|
|
|
RelativeWorkDir: "infra",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-06-21 10:04:12 +00:00
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestCodePathsNoDependencies(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "codepaths-no-dependencies"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-12-12 19:02:23 +00:00
|
|
|
// Test that the resource stopwatch doesn't contain a negative time.
|
|
|
|
func TestNoNegativeTimingsOnRefresh(t *testing.T) {
|
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
t.Skip("Skip on windows because we lack yarn")
|
|
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir := filepath.Join("empty", "nodejs")
|
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory(dir)
|
|
|
|
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
e.RunCommand("yarn", "install")
|
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
|
|
|
e.RunCommand("pulumi", "stack", "init", "negative-timings")
|
|
|
|
e.RunCommand("pulumi", "stack", "select", "negative-timings")
|
|
|
|
e.RunCommand("pulumi", "up", "--yes")
|
|
|
|
stdout, _ := e.RunCommand("pulumi", "destroy", "--skip-preview", "--refresh=true")
|
|
|
|
// Assert there are no negative times in the output.
|
|
|
|
assert.NotContainsf(t, stdout, " (-",
|
|
|
|
"`pulumi destroy --skip-preview --refresh=true` contains a negative time")
|
|
|
|
}
|
|
|
|
|
2021-08-27 17:39:49 +00:00
|
|
|
// Test that the about command works as expected. Because about parses the
|
|
|
|
// results of each runtime independently, we have an integration test in each
|
|
|
|
// language.
|
|
|
|
func TestAboutNodeJS(t *testing.T) {
|
2021-08-31 05:53:16 +00:00
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
t.Skip("Skip on windows because we lack yarn")
|
|
|
|
}
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2021-08-31 05:53:16 +00:00
|
|
|
|
2021-08-27 17:39:49 +00:00
|
|
|
dir := filepath.Join("about", "nodejs")
|
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory(dir)
|
2021-08-31 05:53:16 +00:00
|
|
|
|
2021-08-27 17:39:49 +00:00
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
e.RunCommand("yarn", "install")
|
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
2021-08-28 04:31:46 +00:00
|
|
|
e.RunCommand("pulumi", "stack", "init", "about-nodejs")
|
2021-08-27 23:04:42 +00:00
|
|
|
e.RunCommand("pulumi", "stack", "select", "about-nodejs")
|
2021-08-30 19:55:30 +00:00
|
|
|
stdout, stderr := e.RunCommand("pulumi", "about")
|
2021-08-27 17:39:49 +00:00
|
|
|
e.RunCommand("pulumi", "stack", "rm", "--yes")
|
|
|
|
// Assert we parsed the dependencies
|
2021-08-30 19:55:30 +00:00
|
|
|
assert.Containsf(t, stdout, "@types/node",
|
|
|
|
"Did not contain expected output. stderr: \n%q", stderr)
|
2021-08-27 17:39:49 +00:00
|
|
|
}
|
2021-11-15 23:42:04 +00:00
|
|
|
|
|
|
|
func TestConstructOutputValuesNode(t *testing.T) {
|
2023-12-02 17:16:09 +00:00
|
|
|
t.Parallel()
|
2021-11-15 23:42:04 +00:00
|
|
|
testConstructOutputValues(t, "nodejs", "@pulumi/pulumi")
|
|
|
|
}
|
2021-11-22 19:42:39 +00:00
|
|
|
|
|
|
|
func TestTSConfigOption(t *testing.T) {
|
|
|
|
if runtime.GOOS == WindowsOS {
|
|
|
|
t.Skip("Skip on windows because we lack yarn")
|
|
|
|
}
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
2021-11-22 19:42:39 +00:00
|
|
|
|
|
|
|
e := ptesting.NewEnvironment(t)
|
|
|
|
defer func() {
|
|
|
|
if !t.Failed() {
|
|
|
|
e.DeleteEnvironment()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
e.ImportDirectory("tsconfig")
|
|
|
|
|
|
|
|
e.RunCommand("yarn", "link", "@pulumi/pulumi")
|
|
|
|
e.RunCommand("yarn", "install")
|
|
|
|
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
|
|
|
e.RunCommand("pulumi", "stack", "select", "tsconfg", "--create")
|
|
|
|
e.RunCommand("pulumi", "preview")
|
|
|
|
}
|
2022-09-13 22:16:19 +00:00
|
|
|
|
2022-09-14 17:01:42 +00:00
|
|
|
// This tests that despite an exception, that the snapshot is still written.
|
2022-09-13 22:16:19 +00:00
|
|
|
func TestUnsafeSnapshotManagerRetainsResourcesOnError(t *testing.T) {
|
2024-01-30 18:31:49 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
t.Run("Check with experimental flag", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("unsafe_snapshot_tests", "bad_resource"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Env: []string{
|
|
|
|
"PULUMI_EXPERIMENTAL=1",
|
|
|
|
"PULUMI_SKIP_CHECKPOINTS=1",
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
// The program throws an exception and 1 resource fails to be created.
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure the checkpoint contains the 1003 other resources that were created
|
|
|
|
// - stack
|
|
|
|
// - provider
|
|
|
|
// - `base` resource
|
|
|
|
// - 1000 resources(via a for loop)
|
|
|
|
// - NOT a resource that failed to be created dependent on the `base` resource output
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
assert.Equal(t, 3+1000, len(stackInfo.Deployment.Resources))
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
t.Run("Check without experimental flag", func(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("unsafe_snapshot_tests", "bad_resource"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Env: []string{
|
|
|
|
"PULUMI_EXPERIMENTAL=0",
|
|
|
|
"PULUMI_SKIP_CHECKPOINTS=1",
|
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
// The program throws an exception and 1 resource fails to be created.
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
|
|
|
|
// Ensure the checkpoint contains the 1003 other resources that were created
|
|
|
|
// - stack
|
|
|
|
// - provider
|
|
|
|
// - `base` resource
|
|
|
|
// - 1000 resources(via a for loop)
|
|
|
|
// - NOT a resource that failed to be created dependent on the `base` resource output
|
|
|
|
assert.NotNil(t, stackInfo.Deployment)
|
|
|
|
assert.Equal(t, 3+1000, len(stackInfo.Deployment.Resources))
|
|
|
|
},
|
|
|
|
})
|
2022-09-13 22:16:19 +00:00
|
|
|
})
|
|
|
|
}
|
2022-11-30 19:25:07 +00:00
|
|
|
|
|
|
|
// TestResourceRefsGetResourceNode tests that invoking the built-in 'pulumi:pulumi:getResource' function
|
|
|
|
// returns resource references for any resource reference in a resource's state.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2022-11-30 19:25:07 +00:00
|
|
|
func TestResourceRefsGetResourceNode(t *testing.T) {
|
2022-12-19 14:29:57 +00:00
|
|
|
t.Skip() // TODO[pulumi/pulumi#11677]
|
|
|
|
|
2022-11-30 19:25:07 +00:00
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("resource_refs_get_resource", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
|
|
|
|
// TestDeletedWithNode tests the DeletedWith resource option.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
func TestDeletedWithNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("deleted_with", "nodejs"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
LocalProviders: []integration.LocalDependency{
|
2022-11-23 09:53:41 +00:00
|
|
|
{Package: "testprovider", Path: filepath.Join("..", "testprovider")},
|
[sdk/{go,nodejs,python}] Fix DeletedWith resource option
This change fixes the `DeletedWith` resource option in the Go, Node.js,
and Python SDKs and adds tests.
This feature was a community contribution and while there were engine
tests included with the original PR, there weren't any tests confirming
the functionality worked correctly from each SDK.
Here's a summary of the fixes:
* Go: The `DeletedWith` resource option was never usable as it accepted
a URN instead of a Resource. We discussed this internally a while back
and decided to go ahead and fix this. (Note: While changing the
signature is technically a breaking change, the feature is currently
unusable, so the change would not break anyone, so there's no need to
wait for a major version bump.)
* Node.js: The `deletedWith` resource option did not work at all from
the Node.js SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
* Python: The `deleted_with` resource option did not work at all from
the Python SDK because it was incorrectly passing the resource object
itself in the RegisterResource request, rather than the resource's
URN.
A `FailsOnDelete` resource has been added to the testprovider, which
will fail when its `Delete` gRPC is called. The tests use this to ensure
`Delete` is not called for resources of this type with the `DeletedWith`
option specified.
2023-01-16 00:19:26 +00:00
|
|
|
},
|
|
|
|
Quick: true,
|
|
|
|
})
|
|
|
|
}
|
2023-01-13 23:58:49 +00:00
|
|
|
|
|
|
|
// Tests custom resource type name of dynamic provider.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-01-13 23:58:49 +00:00
|
|
|
func TestCustomResourceTypeNameDynamicNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
2022-11-23 09:53:41 +00:00
|
|
|
Dir: filepath.Join("dynamic", "nodejs-resource-type-name"),
|
2023-01-13 23:58:49 +00:00
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
urnOut := stack.Outputs["urn"].(string)
|
|
|
|
urn := resource.URN(urnOut)
|
|
|
|
typ := urn.Type().String()
|
|
|
|
assert.Equal(t, "pulumi-nodejs:dynamic/custom-provider:CustomResource", typ)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2023-02-28 17:06:15 +00:00
|
|
|
|
2023-03-29 10:21:51 +00:00
|
|
|
// Tests errors in dynamic provider methods
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-03-29 10:21:51 +00:00
|
|
|
func TestErrorCreateDynamicNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("dynamic", "nodejs-error-create"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExpectFailure: true,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
foundError := false
|
|
|
|
for _, event := range stack.Events {
|
|
|
|
if event.ResOpFailedEvent != nil {
|
|
|
|
foundError = true
|
|
|
|
assert.Equal(t, apitype.OpType("create"), event.ResOpFailedEvent.Metadata.Op)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, foundError, "Did not see create error")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-28 17:06:15 +00:00
|
|
|
// Regression test for https://github.com/pulumi/pulumi/issues/12301
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-02-28 17:06:15 +00:00
|
|
|
func TestRegression12301Node(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "regression-12301"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
PostPrepareProject: func(project *engine.Projinfo) error {
|
|
|
|
// Move the bad JSON file up one directory
|
|
|
|
jsonPath := filepath.Join(project.Root, "regression-12301.json")
|
|
|
|
dirName := filepath.Base(project.Root)
|
|
|
|
newPath := filepath.Join(project.Root, "..", dirName+".json")
|
|
|
|
return os.Rename(jsonPath, newPath)
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Len(t, stack.Outputs, 1)
|
|
|
|
assert.Contains(t, stack.Outputs, "bar")
|
|
|
|
assert.Equal(t, 3.0, stack.Outputs["bar"].(float64))
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2023-03-31 10:22:50 +00:00
|
|
|
|
|
|
|
// Tests provider config is passed through to provider processes.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-03-31 10:22:50 +00:00
|
|
|
func TestPulumiConfig(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("dynamic", "nodejs-pulumi-config"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Config: map[string]string{
|
|
|
|
"pulumi-nodejs:id": "testing123",
|
|
|
|
},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Len(t, stack.Outputs, 1)
|
|
|
|
assert.Contains(t, stack.Outputs, "rid")
|
|
|
|
assert.Equal(t, "testing123", stack.Outputs["rid"].(string))
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2023-04-05 18:43:12 +00:00
|
|
|
|
|
|
|
func TestConstructProviderPropagationNode(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testConstructProviderPropagation(t, "nodejs", []string{"@pulumi/pulumi"})
|
|
|
|
}
|
2023-06-26 06:20:14 +00:00
|
|
|
|
|
|
|
func TestConstructProviderExplicitNode(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testConstructProviderExplicit(t, "nodejs", []string{"@pulumi/pulumi"})
|
|
|
|
}
|
2023-06-29 11:33:52 +00:00
|
|
|
|
|
|
|
// Regression test for https://github.com/pulumi/pulumi/issues/7376
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-06-29 11:33:52 +00:00
|
|
|
func TestUndefinedStackOutputNode(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("nodejs", "undefined-stack-output"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
assert.Equal(t, nil, stack.Outputs["nil"])
|
|
|
|
assert.Equal(t, []interface{}{0.0, nil, nil}, stack.Outputs["list"])
|
|
|
|
assert.Equal(t, map[string]interface{}{
|
|
|
|
"nil2": nil,
|
|
|
|
"number2": 0.0,
|
|
|
|
}, stack.Outputs["map"])
|
|
|
|
|
|
|
|
var found bool
|
|
|
|
for _, event := range stack.Events {
|
|
|
|
if event.DiagnosticEvent != nil {
|
|
|
|
if event.DiagnosticEvent.Severity == "warning" &&
|
|
|
|
strings.Contains(event.DiagnosticEvent.Message, "will not show as a stack output") {
|
|
|
|
|
|
|
|
assert.Equal(t,
|
|
|
|
"Undefined value (undef) will not show as a stack output.\n",
|
|
|
|
event.DiagnosticEvent.Message)
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.True(t, found, "Did not see undef warning")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2023-10-12 16:39:26 +00:00
|
|
|
|
|
|
|
// Tests basic environments from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-10-12 16:39:26 +00:00
|
|
|
func TestEnvironmentsBasicNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("environments_basic"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
RequireService: true,
|
|
|
|
CreateEnvironments: []integration.Environment{{
|
|
|
|
Name: "basic",
|
|
|
|
Definition: map[string]any{
|
|
|
|
"values": map[string]any{
|
|
|
|
"pulumiConfig": map[string]any{
|
|
|
|
"aConfigValue": "this value is a value",
|
|
|
|
"bEncryptedSecret": map[string]any{
|
|
|
|
"fn::secret": "this super secret is encrypted",
|
|
|
|
},
|
|
|
|
"outer": map[string]any{
|
|
|
|
"inner": "value",
|
|
|
|
},
|
|
|
|
"names": []any{"a", "b", "c", map[string]any{"fn::secret": "super secret name"}},
|
|
|
|
"servers": []any{
|
|
|
|
map[string]any{
|
|
|
|
"port": 80,
|
|
|
|
"host": "example",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"a": map[string]any{
|
|
|
|
"b": []any{
|
|
|
|
map[string]any{"c": true},
|
|
|
|
map[string]any{"c": false},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"tokens": []any{
|
|
|
|
map[string]any{
|
|
|
|
"fn::secret": "shh",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"foo": map[string]any{
|
|
|
|
"bar": map[string]any{
|
|
|
|
"fn::secret": "don't tell",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
Environments: []string{"basic"},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests merged environments from the perspective of a Pulumi program.
|
2023-12-02 17:16:09 +00:00
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
2023-10-12 16:39:26 +00:00
|
|
|
func TestEnvironmentsMergeNodeJS(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("environments_merge"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
Quick: true,
|
|
|
|
RequireService: true,
|
|
|
|
CreateEnvironments: []integration.Environment{
|
|
|
|
{
|
|
|
|
Name: "merge-0",
|
|
|
|
Definition: map[string]any{
|
|
|
|
"values": map[string]any{
|
|
|
|
"pulumiConfig": map[string]any{
|
|
|
|
"outer": map[string]any{
|
|
|
|
"inner": "not-a-value",
|
|
|
|
},
|
|
|
|
"names": []any{"a", "b", "c", map[string]any{"fn::secret": "super secret name"}},
|
|
|
|
"servers": []any{
|
|
|
|
map[string]any{
|
|
|
|
"port": 80,
|
|
|
|
"host": "example",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "merge-1",
|
|
|
|
Definition: map[string]any{
|
|
|
|
"values": map[string]any{
|
|
|
|
"pulumiConfig": map[string]any{
|
|
|
|
"a": map[string]any{
|
|
|
|
"b": []any{
|
|
|
|
map[string]any{"c": true},
|
|
|
|
map[string]any{"c": false},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"tokens": []any{
|
|
|
|
map[string]any{
|
|
|
|
"fn::secret": "shh",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"foo": map[string]any{
|
|
|
|
"bar": "not so secret",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Environments: []string{"merge-0", "merge-1"},
|
|
|
|
Config: map[string]string{
|
|
|
|
"aConfigValue": "this value is a value",
|
|
|
|
},
|
|
|
|
Secrets: map[string]string{
|
|
|
|
"bEncryptedSecret": "this super secret is encrypted",
|
|
|
|
},
|
|
|
|
OrderedConfig: []integration.ConfigValue{
|
|
|
|
{Key: "outer.inner", Value: "value", Path: true},
|
|
|
|
{Key: "foo.bar", Value: "don't tell", Path: true, Secret: true},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
Support serialising reserved NodeJS identifiers (#15879)
This commit addresses part of #11942, in which we fail to serialise
closures whose code would use reserved identifiers like `exports`. This
is due to a change we made where module imports are hoisted to avoid
importing the same module multiple times. Previously, code adopted a
strategy of passing all dependencies using a `with` statement, viz.:
```typescript
function x() {
return (function () {
with({ fooBarBaz: require("foo/bar/baz"), ... }) {
// Use of fooBarBaz
}
}).apply(...)
}
function y() {
return (function () {
with({ fooBarBaz: require("foo/bar/baz"), ... }) {
// Use of fooBarBaz
}
}).apply(...)
}
```
This was changed to remove the duplicate imports, yielding code like:
```typescript
const fooBarBaz = require("foo/bar/baz")
function x() {
return (function () {
with({ ... }) {
// Use of fooBarBaz
}
}).apply(...)
}
function y() {
return (function () {
with({ ... }) {
// Use of fooBarBaz
}
}).apply(...)
}
```
However, while the previous approach would work with reserved
identifiers such as `exports` (`with({ exports, ... }) { ... }` is
perfectly acceptable), the new one does not (`const exports = ...` is
not acceptable since NodeJS will not allow redeclaration of the
`exports` global).
This commit combines the two approaches. Modules are only imported once,
but if an import would use a reserved identifier, we generate a fresh
non-conflicting identifier and alias this using a `with` statement. For
example:
```typescript
const fooBarBaz = require("foo/bar/baz")
// __pulumi_closure_import_exports is generated to avoid shadowing the reserved "exports"
const __pulumi_closure_import_exports = require("some/other/module")
function x() {
return (function () {
with({ exports: __pulumi_closure_import_exports, ... }) {
// Use of fooBarBaz and exports
}
}).apply(...)
}
```
Note that it is not expected that #11942 will be solved in its entirety.
While this commit fixes code that introduces identifiers like `exports`,
the introduction in question in that issue is caused by the use of
`pulumi.output(...)` in the constructor of a dynamic resource provider.
Since dynamic resource providers are implemented under the hood by
serialising their code, we attempt to serialise `pulumi.output` and its
dependency chain. This commit allows us to make more progress in that
regard, but other things go wrong thereafter. Fixing the deeper issue
that underpins #11942 (and likely other challenges with dynamic
providers) is probably a more involved piece of work.
2024-04-10 15:12:43 +00:00
|
|
|
|
|
|
|
// Tests errors that would occur when generating code that shadows reserved
|
|
|
|
// identifiers (e.g. "const exports = ...").
|
|
|
|
//
|
|
|
|
//nolint:paralleltest // ProgramTest calls t.Parallel()
|
|
|
|
func TestNodeJSReservedIdentifierShadowing(t *testing.T) {
|
|
|
|
integration.ProgramTest(t, &integration.ProgramTestOptions{
|
|
|
|
Dir: filepath.Join("dynamic", "nodejs-reserved-identifier-shadowing"),
|
|
|
|
Dependencies: []string{"@pulumi/pulumi"},
|
|
|
|
ExpectFailure: false,
|
|
|
|
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
|
|
|
|
noError := true
|
|
|
|
for _, event := range stack.Events {
|
|
|
|
if event.ResOpFailedEvent != nil {
|
|
|
|
noError = false
|
|
|
|
assert.Equal(t, apitype.OpType("create"), event.ResOpFailedEvent.Metadata.Op)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.True(t, noError, "An error occurred when testing shadowing of reserved identifiers")
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|