github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/engine/lifecycletest/pulumi_test.go (about)

     1  // Copyright 2016-2022, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // nolint: goconst
    16  package lifecycletest
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"regexp"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    28  
    29  	"github.com/blang/semver"
    30  	pbempty "github.com/golang/protobuf/ptypes/empty"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  	"google.golang.org/grpc"
    35  	"google.golang.org/grpc/codes"
    36  	"google.golang.org/grpc/status"
    37  
    38  	"github.com/pulumi/pulumi/pkg/v3/engine"
    39  	. "github.com/pulumi/pulumi/pkg/v3/engine"
    40  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
    41  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy/deploytest"
    42  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
    43  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
    44  	"github.com/pulumi/pulumi/sdk/v3/go/common/display"
    45  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    46  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
    47  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    48  	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
    49  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    50  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
    51  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
    52  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil/rpcerror"
    53  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    54  	pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
    55  )
    56  
    57  func SuccessfulSteps(entries JournalEntries) []deploy.Step {
    58  	var steps []deploy.Step
    59  	for _, entry := range entries {
    60  		if entry.Kind == JournalEntrySuccess {
    61  			steps = append(steps, entry.Step)
    62  		}
    63  	}
    64  	return steps
    65  }
    66  
    67  type StepSummary struct {
    68  	Op  display.StepOp
    69  	URN resource.URN
    70  }
    71  
    72  func AssertSameSteps(t *testing.T, expected []StepSummary, actual []deploy.Step) bool {
    73  	assert.Equal(t, len(expected), len(actual))
    74  	for _, exp := range expected {
    75  		act := actual[0]
    76  		actual = actual[1:]
    77  
    78  		if !assert.Equal(t, exp.Op, act.Op()) || !assert.Equal(t, exp.URN, act.URN()) {
    79  			return false
    80  		}
    81  	}
    82  	return true
    83  }
    84  
    85  func ExpectDiagMessage(t *testing.T, messagePattern string) ValidateFunc {
    86  	validate := func(
    87  		project workspace.Project, target deploy.Target,
    88  		entries JournalEntries, events []Event,
    89  		res result.Result) result.Result {
    90  
    91  		assert.NotNil(t, res)
    92  
    93  		for i := range events {
    94  			if events[i].Type == "diag" {
    95  				payload := events[i].Payload().(engine.DiagEventPayload)
    96  				match, err := regexp.MatchString(messagePattern, payload.Message)
    97  				if err != nil {
    98  					return result.FromError(err)
    99  				}
   100  				if match {
   101  					return nil
   102  				}
   103  				return result.Errorf("Unexpected diag message: %s", payload.Message)
   104  			}
   105  		}
   106  		return result.Error("Expected a diagnostic message, got none")
   107  	}
   108  	return validate
   109  }
   110  
   111  func pickURN(t *testing.T, urns []resource.URN, names []string, target string) resource.URN {
   112  	assert.Equal(t, len(urns), len(names))
   113  	assert.Contains(t, names, target)
   114  
   115  	for i, name := range names {
   116  		if name == target {
   117  			return urns[i]
   118  		}
   119  	}
   120  
   121  	t.Fatalf("Could not find target: %v in %v", target, names)
   122  	return ""
   123  }
   124  
   125  func TestMain(m *testing.M) {
   126  	grpcDefault := flag.Bool("grpc-plugins", false, "enable or disable gRPC providers by default")
   127  
   128  	flag.Parse()
   129  
   130  	if *grpcDefault {
   131  		deploytest.UseGrpcPluginsByDefault = true
   132  	}
   133  
   134  	os.Exit(m.Run())
   135  }
   136  
   137  func TestEmptyProgramLifecycle(t *testing.T) {
   138  	t.Parallel()
   139  
   140  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, _ *deploytest.ResourceMonitor) error {
   141  		return nil
   142  	})
   143  	host := deploytest.NewPluginHost(nil, nil, program)
   144  
   145  	p := &TestPlan{
   146  		Options: UpdateOptions{Host: host},
   147  		Steps:   MakeBasicLifecycleSteps(t, 0),
   148  	}
   149  	p.Run(t, nil)
   150  }
   151  
   152  func TestSingleResourceDiffUnavailable(t *testing.T) {
   153  	t.Parallel()
   154  
   155  	loaders := []*deploytest.ProviderLoader{
   156  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   157  			return &deploytest.Provider{
   158  				DiffF: func(urn resource.URN, id resource.ID,
   159  					olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
   160  
   161  					return plugin.DiffResult{}, plugin.DiffUnavailable("diff unavailable")
   162  				},
   163  			}, nil
   164  		}),
   165  	}
   166  
   167  	inputs := resource.PropertyMap{}
   168  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   169  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   170  			Inputs: inputs,
   171  		})
   172  		assert.NoError(t, err)
   173  		return nil
   174  	})
   175  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   176  
   177  	p := &TestPlan{
   178  		Options: UpdateOptions{Host: host},
   179  	}
   180  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
   181  
   182  	// Run the initial update.
   183  	project := p.GetProject()
   184  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
   185  	assert.Nil(t, res)
   186  
   187  	// Now run a preview. Expect a warning because the diff is unavailable.
   188  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, true, p.BackendClient,
   189  		func(_ workspace.Project, _ deploy.Target, _ JournalEntries,
   190  			events []Event, res result.Result) result.Result {
   191  
   192  			found := false
   193  			for _, e := range events {
   194  				if e.Type == DiagEvent {
   195  					p := e.Payload().(DiagEventPayload)
   196  					if p.URN == resURN && p.Severity == diag.Warning && p.Message == "<{%reset%}>diff unavailable<{%reset%}>\n" {
   197  						found = true
   198  						break
   199  					}
   200  				}
   201  			}
   202  			assert.True(t, found)
   203  			return res
   204  		})
   205  	assert.Nil(t, res)
   206  }
   207  
   208  // Test that ensures that we log diagnostics for resources that receive an error from Check. (Note that this
   209  // is distinct from receiving non-error failures from Check.)
   210  func TestCheckFailureRecord(t *testing.T) {
   211  	t.Parallel()
   212  
   213  	loaders := []*deploytest.ProviderLoader{
   214  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   215  			return &deploytest.Provider{
   216  				CheckF: func(urn resource.URN,
   217  					olds, news resource.PropertyMap, randomSeed []byte) (resource.PropertyMap, []plugin.CheckFailure, error) {
   218  					return nil, nil, errors.New("oh no, check had an error")
   219  				},
   220  			}, nil
   221  		}),
   222  	}
   223  
   224  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   225  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   226  		assert.Error(t, err)
   227  		return err
   228  	})
   229  
   230  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   231  	p := &TestPlan{
   232  		Options: UpdateOptions{Host: host},
   233  		Steps: []TestStep{{
   234  			Op:            Update,
   235  			ExpectFailure: true,
   236  			SkipPreview:   true,
   237  			Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   238  				evts []Event, res result.Result) result.Result {
   239  
   240  				sawFailure := false
   241  				for _, evt := range evts {
   242  					if evt.Type == DiagEvent {
   243  						e := evt.Payload().(DiagEventPayload)
   244  						msg := colors.Never.Colorize(e.Message)
   245  						sawFailure = msg == "oh no, check had an error\n" && e.Severity == diag.Error
   246  					}
   247  				}
   248  
   249  				assert.True(t, sawFailure)
   250  				return res
   251  			},
   252  		}},
   253  	}
   254  
   255  	p.Run(t, nil)
   256  }
   257  
   258  // Test that checks that we emit diagnostics for properties that check says are invalid.
   259  func TestCheckFailureInvalidPropertyRecord(t *testing.T) {
   260  	t.Parallel()
   261  
   262  	loaders := []*deploytest.ProviderLoader{
   263  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   264  			return &deploytest.Provider{
   265  				CheckF: func(urn resource.URN,
   266  					olds, news resource.PropertyMap, randomSeed []byte) (resource.PropertyMap, []plugin.CheckFailure, error) {
   267  					return nil, []plugin.CheckFailure{{
   268  						Property: "someprop",
   269  						Reason:   "field is not valid",
   270  					}}, nil
   271  				},
   272  			}, nil
   273  		}),
   274  	}
   275  
   276  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   277  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   278  		assert.Error(t, err)
   279  		return err
   280  	})
   281  
   282  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   283  	p := &TestPlan{
   284  		Options: UpdateOptions{Host: host},
   285  		Steps: []TestStep{{
   286  			Op:            Update,
   287  			ExpectFailure: true,
   288  			SkipPreview:   true,
   289  			Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   290  				evts []Event, res result.Result) result.Result {
   291  
   292  				sawFailure := false
   293  				for _, evt := range evts {
   294  					if evt.Type == DiagEvent {
   295  						e := evt.Payload().(DiagEventPayload)
   296  						msg := colors.Never.Colorize(e.Message)
   297  						sawFailure = strings.Contains(msg, "field is not valid") && e.Severity == diag.Error
   298  						if sawFailure {
   299  							break
   300  						}
   301  					}
   302  				}
   303  
   304  				assert.True(t, sawFailure)
   305  				return res
   306  			},
   307  		}},
   308  	}
   309  
   310  	p.Run(t, nil)
   311  
   312  }
   313  
   314  // Tests that errors returned directly from the language host get logged by the engine.
   315  func TestLanguageHostDiagnostics(t *testing.T) {
   316  	t.Parallel()
   317  
   318  	loaders := []*deploytest.ProviderLoader{
   319  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   320  			return &deploytest.Provider{}, nil
   321  		}),
   322  	}
   323  
   324  	errorText := "oh no"
   325  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, _ *deploytest.ResourceMonitor) error {
   326  		// Exiting immediately with an error simulates a language exiting immediately with a non-zero exit code.
   327  		return errors.New(errorText)
   328  	})
   329  
   330  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   331  	p := &TestPlan{
   332  		Options: UpdateOptions{Host: host},
   333  		Steps: []TestStep{{
   334  			Op:            Update,
   335  			ExpectFailure: true,
   336  			SkipPreview:   true,
   337  			Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   338  				evts []Event, res result.Result) result.Result {
   339  
   340  				assertIsErrorOrBailResult(t, res)
   341  				sawExitCode := false
   342  				for _, evt := range evts {
   343  					if evt.Type == DiagEvent {
   344  						e := evt.Payload().(DiagEventPayload)
   345  						msg := colors.Never.Colorize(e.Message)
   346  						sawExitCode = strings.Contains(msg, errorText) && e.Severity == diag.Error
   347  						if sawExitCode {
   348  							break
   349  						}
   350  					}
   351  				}
   352  
   353  				assert.True(t, sawExitCode)
   354  				return res
   355  			},
   356  		}},
   357  	}
   358  
   359  	p.Run(t, nil)
   360  }
   361  
   362  type brokenDecrypter struct {
   363  	ErrorMessage string
   364  }
   365  
   366  func (b brokenDecrypter) DecryptValue(_ context.Context, _ string) (string, error) {
   367  	return "", fmt.Errorf(b.ErrorMessage)
   368  }
   369  
   370  func (b brokenDecrypter) BulkDecrypt(_ context.Context, _ []string) (map[string]string, error) {
   371  	return nil, fmt.Errorf(b.ErrorMessage)
   372  }
   373  
   374  // Tests that the engine presents a reasonable error message when a decrypter fails to decrypt a config value.
   375  func TestBrokenDecrypter(t *testing.T) {
   376  	t.Parallel()
   377  
   378  	loaders := []*deploytest.ProviderLoader{
   379  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   380  			return &deploytest.Provider{}, nil
   381  		}),
   382  	}
   383  
   384  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, _ *deploytest.ResourceMonitor) error {
   385  		return nil
   386  	})
   387  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   388  	key := config.MustMakeKey("foo", "bar")
   389  	msg := "decryption failed"
   390  	configMap := make(config.Map)
   391  	configMap[key] = config.NewSecureValue("hunter2")
   392  	p := &TestPlan{
   393  		Options:   UpdateOptions{Host: host},
   394  		Decrypter: brokenDecrypter{ErrorMessage: msg},
   395  		Config:    configMap,
   396  		Steps: []TestStep{{
   397  			Op:            Update,
   398  			ExpectFailure: true,
   399  			SkipPreview:   true,
   400  			Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   401  				evts []Event, res result.Result) result.Result {
   402  
   403  				assertIsErrorOrBailResult(t, res)
   404  				decryptErr := res.Error().(DecryptError)
   405  				assert.Equal(t, key, decryptErr.Key)
   406  				assert.Contains(t, decryptErr.Err.Error(), msg)
   407  				return res
   408  			},
   409  		}},
   410  	}
   411  
   412  	p.Run(t, nil)
   413  }
   414  
   415  func TestBadResourceType(t *testing.T) {
   416  	t.Parallel()
   417  
   418  	loaders := []*deploytest.ProviderLoader{
   419  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   420  			return &deploytest.Provider{}, nil
   421  		}),
   422  	}
   423  
   424  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
   425  		_, _, _, err := mon.RegisterResource("very:bad", "resA", true)
   426  		assert.Error(t, err)
   427  		rpcerr, ok := rpcerror.FromError(err)
   428  		assert.True(t, ok)
   429  		assert.Equal(t, codes.InvalidArgument, rpcerr.Code())
   430  		assert.Contains(t, rpcerr.Message(), "Type 'very:bad' is not a valid type token")
   431  
   432  		_, _, err = mon.ReadResource("very:bad", "someResource", "someId", "", resource.PropertyMap{}, "", "")
   433  		assert.Error(t, err)
   434  		rpcerr, ok = rpcerror.FromError(err)
   435  		assert.True(t, ok)
   436  		assert.Equal(t, codes.InvalidArgument, rpcerr.Code())
   437  		assert.Contains(t, rpcerr.Message(), "Type 'very:bad' is not a valid type token")
   438  
   439  		// Component resources may have any format type.
   440  		_, _, _, noErr := mon.RegisterResource("a:component", "resB", false)
   441  		assert.NoError(t, noErr)
   442  
   443  		_, _, _, noErr = mon.RegisterResource("singlename", "resC", false)
   444  		assert.NoError(t, noErr)
   445  
   446  		return err
   447  	})
   448  
   449  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   450  	p := &TestPlan{
   451  		Options: UpdateOptions{Host: host},
   452  		Steps: []TestStep{{
   453  			Op:            Update,
   454  			ExpectFailure: true,
   455  			SkipPreview:   true,
   456  		}},
   457  	}
   458  
   459  	p.Run(t, nil)
   460  }
   461  
   462  // Tests that provider cancellation occurs as expected.
   463  func TestProviderCancellation(t *testing.T) {
   464  	t.Parallel()
   465  
   466  	const resourceCount = 4
   467  
   468  	// Set up a cancelable context for the refresh operation.
   469  	ctx, cancel := context.WithCancel(context.Background())
   470  
   471  	// Wait for our resource ops, then cancel.
   472  	var ops sync.WaitGroup
   473  	ops.Add(resourceCount)
   474  	go func() {
   475  		ops.Wait()
   476  		cancel()
   477  	}()
   478  
   479  	// Set up an independent cancelable context for the provider's operations.
   480  	provCtx, provCancel := context.WithCancel(context.Background())
   481  	loaders := []*deploytest.ProviderLoader{
   482  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   483  			return &deploytest.Provider{
   484  				CreateF: func(urn resource.URN, inputs resource.PropertyMap, timeout float64,
   485  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
   486  
   487  					// Inform the waiter that we've entered a provider op and wait for cancellation.
   488  					ops.Done()
   489  					<-provCtx.Done()
   490  
   491  					return resource.ID(urn.Name()), resource.PropertyMap{}, resource.StatusOK, nil
   492  				},
   493  				CancelF: func() error {
   494  					provCancel()
   495  					return nil
   496  				},
   497  			}, nil
   498  		}),
   499  	}
   500  
   501  	done := make(chan bool)
   502  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   503  		errors := make([]error, resourceCount)
   504  		var resources sync.WaitGroup
   505  		resources.Add(resourceCount)
   506  		for i := 0; i < resourceCount; i++ {
   507  			go func(idx int) {
   508  				_, _, _, errors[idx] = monitor.RegisterResource("pkgA:m:typA", fmt.Sprintf("res%d", idx), true)
   509  				resources.Done()
   510  			}(i)
   511  		}
   512  		resources.Wait()
   513  		for _, err := range errors {
   514  			assert.NoError(t, err)
   515  		}
   516  		close(done)
   517  		return nil
   518  	})
   519  
   520  	p := &TestPlan{}
   521  	op := TestOp(Update)
   522  	options := UpdateOptions{
   523  		Parallel: resourceCount,
   524  		Host:     deploytest.NewPluginHost(nil, nil, program, loaders...),
   525  	}
   526  	project, target := p.GetProject(), p.GetTarget(t, nil)
   527  
   528  	_, res := op.RunWithContext(ctx, project, target, options, false, nil, nil)
   529  	assertIsErrorOrBailResult(t, res)
   530  
   531  	// Wait for the program to finish.
   532  	<-done
   533  }
   534  
   535  // Tests that a preview works for a stack with pending operations.
   536  func TestPreviewWithPendingOperations(t *testing.T) {
   537  	t.Parallel()
   538  
   539  	p := &TestPlan{}
   540  
   541  	const resType = "pkgA:m:typA"
   542  	urnA := p.NewURN(resType, "resA", "")
   543  
   544  	newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
   545  		return &resource.State{
   546  			Type:         urn.Type(),
   547  			URN:          urn,
   548  			Custom:       true,
   549  			Delete:       delete,
   550  			ID:           id,
   551  			Inputs:       resource.PropertyMap{},
   552  			Outputs:      resource.PropertyMap{},
   553  			Dependencies: dependencies,
   554  		}
   555  	}
   556  
   557  	old := &deploy.Snapshot{
   558  		PendingOperations: []resource.Operation{{
   559  			Resource: newResource(urnA, "0", false),
   560  			Type:     resource.OperationTypeUpdating,
   561  		}},
   562  		Resources: []*resource.State{
   563  			newResource(urnA, "0", false),
   564  		},
   565  	}
   566  
   567  	loaders := []*deploytest.ProviderLoader{
   568  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   569  			return &deploytest.Provider{}, nil
   570  		}),
   571  	}
   572  
   573  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   574  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   575  		assert.NoError(t, err)
   576  		return nil
   577  	})
   578  
   579  	op := TestOp(Update)
   580  	options := UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
   581  	project, target := p.GetProject(), p.GetTarget(t, old)
   582  
   583  	// A preview should succeed despite the pending operations.
   584  	_, res := op.Run(project, target, options, true, nil, nil)
   585  	assert.Nil(t, res)
   586  }
   587  
   588  // Tests that a refresh works for a stack with pending operations.
   589  func TestRefreshWithPendingOperations(t *testing.T) {
   590  	t.Parallel()
   591  
   592  	p := &TestPlan{}
   593  
   594  	const resType = "pkgA:m:typA"
   595  	urnA := p.NewURN(resType, "resA", "")
   596  
   597  	newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
   598  		return &resource.State{
   599  			Type:         urn.Type(),
   600  			URN:          urn,
   601  			Custom:       true,
   602  			Delete:       delete,
   603  			ID:           id,
   604  			Inputs:       resource.PropertyMap{},
   605  			Outputs:      resource.PropertyMap{},
   606  			Dependencies: dependencies,
   607  		}
   608  	}
   609  
   610  	old := &deploy.Snapshot{
   611  		PendingOperations: []resource.Operation{{
   612  			Resource: newResource(urnA, "0", false),
   613  			Type:     resource.OperationTypeUpdating,
   614  		}},
   615  		Resources: []*resource.State{
   616  			newResource(urnA, "0", false),
   617  		},
   618  	}
   619  
   620  	loaders := []*deploytest.ProviderLoader{
   621  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   622  			return &deploytest.Provider{}, nil
   623  		}),
   624  	}
   625  
   626  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   627  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   628  		assert.NoError(t, err)
   629  		return nil
   630  	})
   631  
   632  	op := TestOp(Update)
   633  	options := UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
   634  	project, target := p.GetProject(), p.GetTarget(t, old)
   635  
   636  	// With a refresh, the update should succeed.
   637  	withRefresh := options
   638  	withRefresh.Refresh = true
   639  	new, res := op.Run(project, target, withRefresh, false, nil, nil)
   640  	assert.Nil(t, res)
   641  	assert.Len(t, new.PendingOperations, 0)
   642  
   643  	// Similarly, the update should succeed if performed after a separate refresh.
   644  	new, res = TestOp(Refresh).Run(project, target, options, false, nil, nil)
   645  	assert.Nil(t, res)
   646  	assert.Len(t, new.PendingOperations, 0)
   647  
   648  	_, res = op.Run(project, p.GetTarget(t, new), options, false, nil, nil)
   649  	assert.Nil(t, res)
   650  }
   651  
   652  // Test to make sure that if we pulumi refresh
   653  // while having pending CREATE operations,
   654  // that these are preserved after the refresh.
   655  func TestRefreshPreservesPendingCreateOperations(t *testing.T) {
   656  	t.Parallel()
   657  
   658  	p := &TestPlan{}
   659  
   660  	const resType = "pkgA:m:typA"
   661  	urnA := p.NewURN(resType, "resA", "")
   662  	urnB := p.NewURN(resType, "resB", "")
   663  
   664  	newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
   665  		return &resource.State{
   666  			Type:         urn.Type(),
   667  			URN:          urn,
   668  			Custom:       true,
   669  			Delete:       delete,
   670  			ID:           id,
   671  			Inputs:       resource.PropertyMap{},
   672  			Outputs:      resource.PropertyMap{},
   673  			Dependencies: dependencies,
   674  		}
   675  	}
   676  
   677  	// Notice here, we have two pending operations: update and create
   678  	// After a refresh, only the pending CREATE operation should
   679  	// be in the updated snapshot
   680  	resA := newResource(urnA, "0", false)
   681  	resB := newResource(urnB, "0", false)
   682  	old := &deploy.Snapshot{
   683  		PendingOperations: []resource.Operation{
   684  			{
   685  				Resource: resA,
   686  				Type:     resource.OperationTypeUpdating,
   687  			},
   688  			{
   689  				Resource: resB,
   690  				Type:     resource.OperationTypeCreating,
   691  			},
   692  		},
   693  		Resources: []*resource.State{
   694  			resA,
   695  		},
   696  	}
   697  
   698  	loaders := []*deploytest.ProviderLoader{
   699  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   700  			return &deploytest.Provider{}, nil
   701  		}),
   702  	}
   703  
   704  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   705  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   706  		assert.NoError(t, err)
   707  		return nil
   708  	})
   709  
   710  	op := TestOp(Update)
   711  	options := UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
   712  	project, target := p.GetProject(), p.GetTarget(t, old)
   713  
   714  	// With a refresh, the update should succeed.
   715  	withRefresh := options
   716  	withRefresh.Refresh = true
   717  	new, res := op.Run(project, target, withRefresh, false, nil, nil)
   718  	assert.Nil(t, res)
   719  	// Assert that pending CREATE operation was preserved
   720  	assert.Len(t, new.PendingOperations, 1)
   721  	assert.Equal(t, resource.OperationTypeCreating, new.PendingOperations[0].Type)
   722  	assert.Equal(t, urnB, new.PendingOperations[0].Resource.URN)
   723  }
   724  
   725  func findPendingOperationsByType(opType resource.OperationType, snapshot *deploy.Snapshot) []resource.Operation {
   726  	var operations []resource.Operation
   727  	for _, operation := range snapshot.PendingOperations {
   728  		if operation.Type == opType {
   729  			operations = append(operations, operation)
   730  		}
   731  	}
   732  	return operations
   733  }
   734  
   735  // Update succeeds but gives a warning when there are pending operations
   736  func TestUpdateShowsWarningWithPendingOperations(t *testing.T) {
   737  	t.Parallel()
   738  
   739  	p := &TestPlan{}
   740  
   741  	const resType = "pkgA:m:typA"
   742  	urnA := p.NewURN(resType, "resA", "")
   743  	urnB := p.NewURN(resType, "resB", "")
   744  
   745  	newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
   746  		return &resource.State{
   747  			Type:         urn.Type(),
   748  			URN:          urn,
   749  			Custom:       true,
   750  			Delete:       delete,
   751  			ID:           id,
   752  			Inputs:       resource.PropertyMap{},
   753  			Outputs:      resource.PropertyMap{},
   754  			Dependencies: dependencies,
   755  		}
   756  	}
   757  
   758  	old := &deploy.Snapshot{
   759  		PendingOperations: []resource.Operation{
   760  			{
   761  				Resource: newResource(urnA, "0", false),
   762  				Type:     resource.OperationTypeUpdating,
   763  			},
   764  			{
   765  				Resource: newResource(urnB, "1", false),
   766  				Type:     resource.OperationTypeCreating,
   767  			}},
   768  		Resources: []*resource.State{
   769  			newResource(urnA, "0", false),
   770  		},
   771  	}
   772  
   773  	loaders := []*deploytest.ProviderLoader{
   774  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   775  			return &deploytest.Provider{}, nil
   776  		}),
   777  	}
   778  
   779  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
   780  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
   781  		assert.NoError(t, err)
   782  		return nil
   783  	})
   784  
   785  	op := TestOp(Update)
   786  	options := UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
   787  	project, target := p.GetProject(), p.GetTarget(t, old)
   788  
   789  	// The update should succeed but give a warning
   790  	initialPartOfMessage := "Attempting to deploy or update resources with 1 pending operations from previous deployment."
   791  	validate := func(
   792  		project workspace.Project, target deploy.Target,
   793  		entries JournalEntries, events []Event,
   794  		res result.Result) result.Result {
   795  		for i := range events {
   796  			if events[i].Type == "diag" {
   797  				payload := events[i].Payload().(engine.DiagEventPayload)
   798  
   799  				if payload.Severity == "warning" && strings.Contains(payload.Message, initialPartOfMessage) {
   800  					return nil
   801  				}
   802  				return result.Errorf("Unexpected warning diag message: %s", payload.Message)
   803  			}
   804  		}
   805  		return result.Error("Expected a diagnostic message, got none")
   806  	}
   807  
   808  	new, _ := op.Run(project, target, options, false, nil, validate)
   809  	assert.NotNil(t, new)
   810  
   811  	assert.Equal(t, resource.OperationTypeCreating, new.PendingOperations[0].Type)
   812  
   813  	// Assert that CREATE pending operations are retained
   814  	// TODO: should revisit whether non-CREATE pending operations should also be retained
   815  	assert.Equal(t, 1, len(new.PendingOperations))
   816  	createOperations := findPendingOperationsByType(resource.OperationTypeCreating, new)
   817  	assert.Equal(t, 1, len(createOperations))
   818  	assert.Equal(t, urnB, createOperations[0].Resource.URN)
   819  }
   820  
   821  // Tests that a failed partial update causes the engine to persist the resource's old inputs and new outputs.
   822  func TestUpdatePartialFailure(t *testing.T) {
   823  	t.Parallel()
   824  
   825  	loaders := []*deploytest.ProviderLoader{
   826  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   827  			return &deploytest.Provider{
   828  				DiffF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap,
   829  					ignoreChanges []string) (plugin.DiffResult, error) {
   830  
   831  					return plugin.DiffResult{
   832  						Changes: plugin.DiffSome,
   833  					}, nil
   834  				},
   835  
   836  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
   837  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
   838  
   839  					outputs := resource.NewPropertyMapFromMap(map[string]interface{}{
   840  						"output_prop": 42,
   841  					})
   842  
   843  					return outputs, resource.StatusPartialFailure, errors.New("update failed to apply")
   844  				},
   845  			}, nil
   846  		}),
   847  	}
   848  
   849  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
   850  		_, _, _, err := mon.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
   851  			Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   852  				"input_prop": "new inputs",
   853  			}),
   854  		})
   855  		return err
   856  	})
   857  
   858  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
   859  	p := &TestPlan{Options: UpdateOptions{Host: host}}
   860  
   861  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
   862  	p.Steps = []TestStep{{
   863  		Op:            Update,
   864  		ExpectFailure: true,
   865  		SkipPreview:   true,
   866  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   867  			evts []Event, res result.Result) result.Result {
   868  
   869  			assertIsErrorOrBailResult(t, res)
   870  			for _, entry := range entries {
   871  				switch urn := entry.Step.URN(); urn {
   872  				case resURN:
   873  					assert.Equal(t, deploy.OpUpdate, entry.Step.Op())
   874  					switch entry.Kind {
   875  					case JournalEntryBegin:
   876  						continue
   877  					case JournalEntrySuccess:
   878  						inputs := entry.Step.New().Inputs
   879  						outputs := entry.Step.New().Outputs
   880  						assert.Len(t, inputs, 1)
   881  						assert.Len(t, outputs, 1)
   882  						assert.Equal(t,
   883  							resource.NewStringProperty("old inputs"), inputs[resource.PropertyKey("input_prop")])
   884  						assert.Equal(t,
   885  							resource.NewNumberProperty(42), outputs[resource.PropertyKey("output_prop")])
   886  					default:
   887  						t.Fatalf("unexpected journal operation: %d", entry.Kind)
   888  					}
   889  				}
   890  			}
   891  
   892  			return res
   893  		},
   894  	}}
   895  
   896  	old := &deploy.Snapshot{
   897  		Resources: []*resource.State{
   898  			{
   899  				Type:   resURN.Type(),
   900  				URN:    resURN,
   901  				Custom: true,
   902  				ID:     "1",
   903  				Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   904  					"input_prop": "old inputs",
   905  				}),
   906  				Outputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   907  					"output_prop": 1,
   908  				}),
   909  			},
   910  		},
   911  	}
   912  
   913  	p.Run(t, old)
   914  }
   915  
   916  // Tests that the StackReference resource works as intended,
   917  func TestStackReference(t *testing.T) {
   918  	t.Parallel()
   919  
   920  	loaders := []*deploytest.ProviderLoader{}
   921  
   922  	// Test that the normal lifecycle works correctly.
   923  	program := deploytest.NewLanguageRuntime(func(info plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
   924  		_, _, state, err := mon.RegisterResource("pulumi:pulumi:StackReference", "other", true, deploytest.ResourceOptions{
   925  			Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   926  				"name": "other",
   927  			}),
   928  		})
   929  		assert.NoError(t, err)
   930  		if !info.DryRun {
   931  			assert.Equal(t, "bar", state["outputs"].ObjectValue()["foo"].StringValue())
   932  		}
   933  		return nil
   934  	})
   935  	p := &TestPlan{
   936  		BackendClient: &deploytest.BackendClient{
   937  			GetStackOutputsF: func(ctx context.Context, name string) (resource.PropertyMap, error) {
   938  				switch name {
   939  				case "other":
   940  					return resource.NewPropertyMapFromMap(map[string]interface{}{
   941  						"foo": "bar",
   942  					}), nil
   943  				default:
   944  					return nil, fmt.Errorf("unknown stack \"%s\"", name)
   945  				}
   946  			},
   947  		},
   948  		Options: UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)},
   949  		Steps:   MakeBasicLifecycleSteps(t, 2),
   950  	}
   951  	p.Run(t, nil)
   952  
   953  	// Test that changes to `name` cause replacement.
   954  	resURN := p.NewURN("pulumi:pulumi:StackReference", "other", "")
   955  	old := &deploy.Snapshot{
   956  		Resources: []*resource.State{
   957  			{
   958  				Type:   resURN.Type(),
   959  				URN:    resURN,
   960  				Custom: true,
   961  				ID:     "1",
   962  				Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   963  					"name": "other2",
   964  				}),
   965  				Outputs: resource.NewPropertyMapFromMap(map[string]interface{}{
   966  					"name":    "other2",
   967  					"outputs": resource.PropertyMap{},
   968  				}),
   969  			},
   970  		},
   971  	}
   972  	p.Steps = []TestStep{{
   973  		Op:          Update,
   974  		SkipPreview: true,
   975  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
   976  			evts []Event, res result.Result) result.Result {
   977  
   978  			assert.Nil(t, res)
   979  			for _, entry := range entries {
   980  				switch urn := entry.Step.URN(); urn {
   981  				case resURN:
   982  					switch entry.Step.Op() {
   983  					case deploy.OpCreateReplacement, deploy.OpDeleteReplaced, deploy.OpReplace:
   984  						// OK
   985  					default:
   986  						t.Fatalf("unexpected journal operation: %v", entry.Step.Op())
   987  					}
   988  				}
   989  			}
   990  
   991  			return res
   992  		},
   993  	}}
   994  	p.Run(t, old)
   995  
   996  	// Test that unknown stacks are handled appropriately.
   997  	program = deploytest.NewLanguageRuntime(func(info plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
   998  		_, _, _, err := mon.RegisterResource("pulumi:pulumi:StackReference", "other", true, deploytest.ResourceOptions{
   999  			Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
  1000  				"name": "rehto",
  1001  			}),
  1002  		})
  1003  		assert.Error(t, err)
  1004  		return err
  1005  	})
  1006  	p.Options = UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
  1007  	p.Steps = []TestStep{{
  1008  		Op:            Update,
  1009  		ExpectFailure: true,
  1010  		SkipPreview:   true,
  1011  	}}
  1012  	p.Run(t, nil)
  1013  
  1014  	// Test that unknown properties cause errors.
  1015  	program = deploytest.NewLanguageRuntime(func(info plugin.RunInfo, mon *deploytest.ResourceMonitor) error {
  1016  		_, _, _, err := mon.RegisterResource("pulumi:pulumi:StackReference", "other", true, deploytest.ResourceOptions{
  1017  			Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
  1018  				"name": "other",
  1019  				"foo":  "bar",
  1020  			}),
  1021  		})
  1022  		assert.Error(t, err)
  1023  		return err
  1024  	})
  1025  	p.Options = UpdateOptions{Host: deploytest.NewPluginHost(nil, nil, program, loaders...)}
  1026  	p.Run(t, nil)
  1027  }
  1028  
  1029  type channelWriter struct {
  1030  	channel chan []byte
  1031  }
  1032  
  1033  func (cw *channelWriter) Write(d []byte) (int, error) {
  1034  	cw.channel <- d
  1035  	return len(d), nil
  1036  }
  1037  
  1038  // Tests that a failed plugin load correctly shuts down the host.
  1039  func TestLoadFailureShutdown(t *testing.T) {
  1040  	t.Parallel()
  1041  
  1042  	// Note that the setup here is a bit baroque, and is intended to replicate the CLI architecture that lead to
  1043  	// issue #2170. That issue--a panic on a closed channel--was caused by the intersection of several design choices:
  1044  	//
  1045  	// - The provider registry loads and configures the set of providers necessary for the resources currently in the
  1046  	//   checkpoint it is processing at plan creation time. Registry creation fails promptly if a provider plugin
  1047  	//   fails to load (e.g. because is binary is missing).
  1048  	// - Provider configuration in the CLI's host happens asynchronously. This is meant to allow the engine to remain
  1049  	//   responsive while plugins configure.
  1050  	// - Providers may call back into the CLI's host for logging. Callbacks are processed as long as the CLI's plugin
  1051  	//   context is open.
  1052  	// - Log events from the CLI's host are delivered to the CLI's diagnostic streams via channels. The CLI closes
  1053  	//   these channels once the engine operation it initiated completes.
  1054  	//
  1055  	// These choices gave rise to the following situation:
  1056  	// 1. The provider registry loads a provider for package A and kicks off its configuration.
  1057  	// 2. The provider registry attempts to load a provider for package B. The load fails, and the provider registry
  1058  	//   creation call fails promptly.
  1059  	// 3. The engine operation requested by the CLI fails promptly because provider registry creation failed.
  1060  	// 4. The CLI shuts down its diagnostic channels.
  1061  	// 5. The provider for package A calls back in to the host to log a message. The host then attempts to deliver
  1062  	//    the message to the CLI's diagnostic channels, causing a panic.
  1063  	//
  1064  	// The fix was to properly close the plugin host during step (3) s.t. the host was no longer accepting callbacks
  1065  	// and would not attempt to send messages to the CLI's diagnostic channels.
  1066  	//
  1067  	// As such, this test attempts to replicate the CLI architecture by using one provider that configures
  1068  	// asynchronously and attempts to call back into the engine and a second provider that fails to load.
  1069  
  1070  	release, done := make(chan bool), make(chan bool)
  1071  	sinkWriter := &channelWriter{channel: make(chan []byte)}
  1072  
  1073  	loaders := []*deploytest.ProviderLoader{
  1074  		deploytest.NewProviderLoaderWithHost("pkgA", semver.MustParse("1.0.0"),
  1075  			func(host plugin.Host) (plugin.Provider, error) {
  1076  				return &deploytest.Provider{
  1077  					ConfigureF: func(news resource.PropertyMap) error {
  1078  						go func() {
  1079  							<-release
  1080  							host.Log(diag.Info, "", "configuring pkgA provider...", 0)
  1081  							close(done)
  1082  						}()
  1083  						return nil
  1084  					},
  1085  				}, nil
  1086  			}),
  1087  		deploytest.NewProviderLoader("pkgB", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1088  			return nil, errors.New("pkgB load failure")
  1089  		}),
  1090  	}
  1091  
  1092  	p := &TestPlan{}
  1093  	provAURN := p.NewProviderURN("pkgA", "default", "")
  1094  	provBURN := p.NewProviderURN("pkgB", "default", "")
  1095  
  1096  	old := &deploy.Snapshot{
  1097  		Resources: []*resource.State{
  1098  			{
  1099  				Type:    provAURN.Type(),
  1100  				URN:     provAURN,
  1101  				Custom:  true,
  1102  				ID:      "0",
  1103  				Inputs:  resource.PropertyMap{},
  1104  				Outputs: resource.PropertyMap{},
  1105  			},
  1106  			{
  1107  				Type:    provBURN.Type(),
  1108  				URN:     provBURN,
  1109  				Custom:  true,
  1110  				ID:      "1",
  1111  				Inputs:  resource.PropertyMap{},
  1112  				Outputs: resource.PropertyMap{},
  1113  			},
  1114  		},
  1115  	}
  1116  
  1117  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1118  		return nil
  1119  	})
  1120  
  1121  	op := TestOp(Update)
  1122  	sink := diag.DefaultSink(sinkWriter, sinkWriter, diag.FormatOptions{Color: colors.Raw})
  1123  	options := UpdateOptions{Host: deploytest.NewPluginHost(sink, sink, program, loaders...)}
  1124  	project, target := p.GetProject(), p.GetTarget(t, old)
  1125  
  1126  	_, res := op.Run(project, target, options, true, nil, nil)
  1127  	assertIsErrorOrBailResult(t, res)
  1128  
  1129  	close(sinkWriter.channel)
  1130  	close(release)
  1131  	<-done
  1132  }
  1133  
  1134  func TestSingleResourceIgnoreChanges(t *testing.T) {
  1135  	t.Parallel()
  1136  
  1137  	var expectedIgnoreChanges []string
  1138  
  1139  	loaders := []*deploytest.ProviderLoader{
  1140  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1141  			return &deploytest.Provider{
  1142  				DiffF: func(urn resource.URN, id resource.ID,
  1143  					olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  1144  
  1145  					assert.Equal(t, expectedIgnoreChanges, ignoreChanges)
  1146  					return plugin.DiffResult{}, nil
  1147  				},
  1148  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  1149  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  1150  
  1151  					assert.Equal(t, expectedIgnoreChanges, ignoreChanges)
  1152  					return resource.PropertyMap{}, resource.StatusOK, nil
  1153  				},
  1154  			}, nil
  1155  		}),
  1156  	}
  1157  
  1158  	updateProgramWithProps := func(snap *deploy.Snapshot, props resource.PropertyMap, ignoreChanges []string,
  1159  		allowedOps []display.StepOp) *deploy.Snapshot {
  1160  		expectedIgnoreChanges = ignoreChanges
  1161  		program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1162  			_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1163  				Inputs:        props,
  1164  				IgnoreChanges: ignoreChanges,
  1165  			})
  1166  			assert.NoError(t, err)
  1167  			return nil
  1168  		})
  1169  		host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1170  		p := &TestPlan{
  1171  			Options: UpdateOptions{Host: host},
  1172  			Steps: []TestStep{
  1173  				{
  1174  					Op: Update,
  1175  					Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  1176  						events []Event, res result.Result) result.Result {
  1177  						for _, event := range events {
  1178  							if event.Type == ResourcePreEvent {
  1179  								payload := event.Payload().(ResourcePreEventPayload)
  1180  								assert.Subset(t, allowedOps, []display.StepOp{payload.Metadata.Op})
  1181  							}
  1182  						}
  1183  						return res
  1184  					},
  1185  				},
  1186  			},
  1187  		}
  1188  		return p.Run(t, snap)
  1189  	}
  1190  
  1191  	snap := updateProgramWithProps(nil, resource.NewPropertyMapFromMap(map[string]interface{}{
  1192  		"a": 1,
  1193  		"b": map[string]interface{}{
  1194  			"c": "foo",
  1195  		},
  1196  	}), []string{"a", "b.c"}, []display.StepOp{deploy.OpCreate})
  1197  
  1198  	// Ensure that a change to an ignored property results in an OpSame
  1199  	snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1200  		"a": 2,
  1201  		"b": map[string]interface{}{
  1202  			"c": "bar",
  1203  		},
  1204  	}), []string{"a", "b.c"}, []display.StepOp{deploy.OpSame})
  1205  
  1206  	// Ensure that a change to an un-ignored property results in an OpUpdate
  1207  	snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1208  		"a": 3,
  1209  		"b": map[string]interface{}{
  1210  			"c": "qux",
  1211  		},
  1212  	}), nil, []display.StepOp{deploy.OpUpdate})
  1213  
  1214  	// Ensure that a removing an ignored property results in an OpSame
  1215  	snap = updateProgramWithProps(snap, resource.PropertyMap{}, []string{"a", "b"}, []display.StepOp{deploy.OpSame})
  1216  
  1217  	// Ensure that a removing an un-ignored property results in an OpUpdate
  1218  	snap = updateProgramWithProps(snap, resource.PropertyMap{}, nil, []display.StepOp{deploy.OpUpdate})
  1219  
  1220  	// Ensure that adding an ignored property results in an OpSame
  1221  	snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1222  		"a": 4,
  1223  		"b": map[string]interface{}{
  1224  			"c": "zed",
  1225  		},
  1226  	}), []string{"a", "b"}, []display.StepOp{deploy.OpSame})
  1227  
  1228  	// Ensure that adding an un-ignored property results in an OpUpdate
  1229  	_ = updateProgramWithProps(snap, resource.PropertyMap{
  1230  		"c": resource.NewNumberProperty(4),
  1231  	}, []string{"a", "b"}, []display.StepOp{deploy.OpUpdate})
  1232  }
  1233  
  1234  func TestIgnoreChangesInvalidPaths(t *testing.T) {
  1235  	t.Parallel()
  1236  
  1237  	loaders := []*deploytest.ProviderLoader{
  1238  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1239  			return &deploytest.Provider{}, nil
  1240  		}),
  1241  	}
  1242  
  1243  	program := func(monitor *deploytest.ResourceMonitor) error {
  1244  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1245  			Inputs: resource.PropertyMap{
  1246  				"foo": resource.NewObjectProperty(resource.PropertyMap{
  1247  					"bar": resource.NewStringProperty("baz"),
  1248  				}),
  1249  				"qux": resource.NewArrayProperty([]resource.PropertyValue{
  1250  					resource.NewStringProperty("zed"),
  1251  				}),
  1252  			},
  1253  		})
  1254  		assert.NoError(t, err)
  1255  		return nil
  1256  	}
  1257  
  1258  	runtime := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1259  		return program(monitor)
  1260  	})
  1261  	host := deploytest.NewPluginHost(nil, nil, runtime, loaders...)
  1262  
  1263  	p := &TestPlan{
  1264  		Options: UpdateOptions{Host: host},
  1265  	}
  1266  
  1267  	project := p.GetProject()
  1268  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  1269  	assert.Nil(t, res)
  1270  
  1271  	program = func(monitor *deploytest.ResourceMonitor) error {
  1272  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1273  			Inputs:        resource.PropertyMap{},
  1274  			IgnoreChanges: []string{"foo.bar"},
  1275  		})
  1276  		assert.Error(t, err)
  1277  		return nil
  1278  	}
  1279  
  1280  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  1281  	assert.NotNil(t, res)
  1282  
  1283  	program = func(monitor *deploytest.ResourceMonitor) error {
  1284  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1285  			Inputs:        resource.PropertyMap{},
  1286  			IgnoreChanges: []string{"qux[0]"},
  1287  		})
  1288  		assert.Error(t, err)
  1289  		return nil
  1290  	}
  1291  
  1292  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  1293  	assert.NotNil(t, res)
  1294  }
  1295  
  1296  type DiffFunc = func(urn resource.URN, id resource.ID,
  1297  	olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error)
  1298  
  1299  func replaceOnChangesTest(t *testing.T, name string, diffFunc DiffFunc) {
  1300  	t.Run(name, func(t *testing.T) {
  1301  		t.Parallel()
  1302  
  1303  		loaders := []*deploytest.ProviderLoader{
  1304  			deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1305  				return &deploytest.Provider{
  1306  					DiffF: diffFunc,
  1307  					UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  1308  						ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  1309  						return news, resource.StatusOK, nil
  1310  					},
  1311  					CreateF: func(urn resource.URN, inputs resource.PropertyMap, timeout float64,
  1312  						preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  1313  						return resource.ID("id123"), inputs, resource.StatusOK, nil
  1314  					},
  1315  				}, nil
  1316  			}),
  1317  		}
  1318  
  1319  		updateProgramWithProps := func(snap *deploy.Snapshot, props resource.PropertyMap, replaceOnChanges []string,
  1320  			allowedOps []display.StepOp) *deploy.Snapshot {
  1321  			program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1322  				_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  1323  					Inputs:           props,
  1324  					ReplaceOnChanges: replaceOnChanges,
  1325  				})
  1326  				assert.NoError(t, err)
  1327  				return nil
  1328  			})
  1329  			host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1330  			p := &TestPlan{
  1331  				Options: UpdateOptions{Host: host},
  1332  				Steps: []TestStep{
  1333  					{
  1334  						Op: Update,
  1335  						Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  1336  							events []Event, res result.Result) result.Result {
  1337  							for _, event := range events {
  1338  								if event.Type == ResourcePreEvent {
  1339  									payload := event.Payload().(ResourcePreEventPayload)
  1340  									assert.Subset(t, allowedOps, []display.StepOp{payload.Metadata.Op})
  1341  								}
  1342  							}
  1343  							return res
  1344  						},
  1345  					},
  1346  				},
  1347  			}
  1348  			return p.Run(t, snap)
  1349  		}
  1350  
  1351  		snap := updateProgramWithProps(nil, resource.NewPropertyMapFromMap(map[string]interface{}{
  1352  			"a": 1,
  1353  			"b": map[string]interface{}{
  1354  				"c": "foo",
  1355  			},
  1356  		}), []string{"a", "b.c"}, []display.StepOp{deploy.OpCreate})
  1357  
  1358  		// Ensure that a change to a replaceOnChange property results in an OpReplace
  1359  		snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1360  			"a": 2,
  1361  			"b": map[string]interface{}{
  1362  				"c": "foo",
  1363  			},
  1364  		}), []string{"a"}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced})
  1365  
  1366  		// Ensure that a change to a nested replaceOnChange property results in an OpReplace
  1367  		snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1368  			"a": 2,
  1369  			"b": map[string]interface{}{
  1370  				"c": "bar",
  1371  			},
  1372  		}), []string{"b.c"}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced})
  1373  
  1374  		// Ensure that a change to any property of a "*" replaceOnChange results in an OpReplace
  1375  		snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1376  			"a": 3,
  1377  			"b": map[string]interface{}{
  1378  				"c": "baz",
  1379  			},
  1380  		}), []string{"*"}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced})
  1381  
  1382  		// Ensure that a change to an non-replaceOnChange property results in an OpUpdate
  1383  		snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1384  			"a": 4,
  1385  			"b": map[string]interface{}{
  1386  				"c": "qux",
  1387  			},
  1388  		}), nil, []display.StepOp{deploy.OpUpdate})
  1389  
  1390  		// We ensure that we are listing to the engine diff function only when the provider function
  1391  		// is nil. We do this by adding some weirdness to the provider diff function.
  1392  		allowed := []display.StepOp{deploy.OpCreateReplacement, deploy.OpReplace, deploy.OpDeleteReplaced}
  1393  		if diffFunc != nil {
  1394  			allowed = []display.StepOp{deploy.OpSame}
  1395  		}
  1396  		snap = updateProgramWithProps(snap, resource.NewPropertyMapFromMap(map[string]interface{}{
  1397  			"a": 42, // 42 is a special value in the "provider" diff function.
  1398  			"b": map[string]interface{}{
  1399  				"c": "qux",
  1400  			},
  1401  		}), []string{"a"}, allowed)
  1402  
  1403  		_ = snap
  1404  	})
  1405  }
  1406  
  1407  func TestReplaceOnChanges(t *testing.T) {
  1408  	t.Parallel()
  1409  
  1410  	// We simulate a provider that has it's own diff function.
  1411  	replaceOnChangesTest(t, "provider diff",
  1412  		func(urn resource.URN, id resource.ID,
  1413  			olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  1414  
  1415  			// To establish a observable difference between the provider and engine diff function,
  1416  			// we treat 42 as an OpSame. We use this to check that the right diff function is being
  1417  			// used.
  1418  			for k, v := range news {
  1419  				if v == resource.NewNumberProperty(42) {
  1420  					news[k] = olds[k]
  1421  				}
  1422  			}
  1423  			diff := olds.Diff(news)
  1424  			if diff == nil {
  1425  				return plugin.DiffResult{Changes: plugin.DiffNone}, nil
  1426  			}
  1427  			detailedDiff := plugin.NewDetailedDiffFromObjectDiff(diff)
  1428  			changedKeys := diff.ChangedKeys()
  1429  
  1430  			return plugin.DiffResult{
  1431  				Changes:      plugin.DiffSome,
  1432  				ChangedKeys:  changedKeys,
  1433  				DetailedDiff: detailedDiff,
  1434  			}, nil
  1435  		})
  1436  
  1437  	// We simulate a provider that does not have it's own diff function. This tests the engines diff
  1438  	// function instead.
  1439  	replaceOnChangesTest(t, "engine diff", nil)
  1440  }
  1441  
  1442  // Resource is an abstract representation of a resource graph
  1443  type Resource struct {
  1444  	t                   tokens.Type
  1445  	name                string
  1446  	children            []Resource
  1447  	props               resource.PropertyMap
  1448  	aliasURNs           []resource.URN
  1449  	aliases             []resource.Alias
  1450  	dependencies        []resource.URN
  1451  	parent              resource.URN
  1452  	deleteBeforeReplace bool
  1453  }
  1454  
  1455  func registerResources(t *testing.T, monitor *deploytest.ResourceMonitor, resources []Resource) error {
  1456  	for _, r := range resources {
  1457  		_, _, _, err := monitor.RegisterResource(r.t, r.name, true, deploytest.ResourceOptions{
  1458  			Parent:              r.parent,
  1459  			Dependencies:        r.dependencies,
  1460  			Inputs:              r.props,
  1461  			DeleteBeforeReplace: &r.deleteBeforeReplace,
  1462  			AliasURNs:           r.aliasURNs,
  1463  			Aliases:             r.aliases,
  1464  		})
  1465  		if err != nil {
  1466  			return err
  1467  		}
  1468  		err = registerResources(t, monitor, r.children)
  1469  		if err != nil {
  1470  			return err
  1471  		}
  1472  	}
  1473  	return nil
  1474  }
  1475  
  1476  func TestAliases(t *testing.T) {
  1477  	t.Parallel()
  1478  
  1479  	loaders := []*deploytest.ProviderLoader{
  1480  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1481  			return &deploytest.Provider{
  1482  				// The `forcesReplacement` key forces replacement and all other keys can update in place
  1483  				DiffF: func(res resource.URN, id resource.ID, olds, news resource.PropertyMap,
  1484  					ignoreChanges []string) (plugin.DiffResult, error) {
  1485  
  1486  					replaceKeys := []resource.PropertyKey{}
  1487  					old, hasOld := olds["forcesReplacement"]
  1488  					new, hasNew := news["forcesReplacement"]
  1489  					if hasOld && !hasNew || hasNew && !hasOld || hasOld && hasNew && old.Diff(new) != nil {
  1490  						replaceKeys = append(replaceKeys, "forcesReplacement")
  1491  					}
  1492  					return plugin.DiffResult{ReplaceKeys: replaceKeys}, nil
  1493  				},
  1494  			}, nil
  1495  		}),
  1496  	}
  1497  
  1498  	updateProgramWithResource := func(
  1499  		snap *deploy.Snapshot, resources []Resource, allowedOps []display.StepOp, expectFailure bool) *deploy.Snapshot {
  1500  		program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1501  			err := registerResources(t, monitor, resources)
  1502  			return err
  1503  		})
  1504  		host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1505  		p := &TestPlan{
  1506  			Options: UpdateOptions{Host: host},
  1507  			Steps: []TestStep{
  1508  				{
  1509  					Op:            Update,
  1510  					ExpectFailure: expectFailure,
  1511  					Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  1512  						events []Event, res result.Result) result.Result {
  1513  						for _, event := range events {
  1514  							if event.Type == ResourcePreEvent {
  1515  								payload := event.Payload().(ResourcePreEventPayload)
  1516  								assert.Subset(t, allowedOps, []display.StepOp{payload.Metadata.Op})
  1517  							}
  1518  						}
  1519  
  1520  						for _, entry := range entries {
  1521  							if entry.Step.Type() == "pulumi:providers:pkgA" {
  1522  								continue
  1523  							}
  1524  							switch entry.Kind {
  1525  							case JournalEntrySuccess:
  1526  								assert.Subset(t, allowedOps, []display.StepOp{entry.Step.Op()})
  1527  							case JournalEntryFailure:
  1528  								assert.Fail(t, "unexpected failure in journal")
  1529  							case JournalEntryBegin:
  1530  							case JournalEntryOutputs:
  1531  							}
  1532  						}
  1533  
  1534  						return res
  1535  					},
  1536  				},
  1537  			},
  1538  		}
  1539  		return p.Run(t, snap)
  1540  	}
  1541  
  1542  	snap := updateProgramWithResource(nil, []Resource{{
  1543  		t:    "pkgA:index:t1",
  1544  		name: "n1",
  1545  	}}, []display.StepOp{deploy.OpCreate}, false)
  1546  
  1547  	// Ensure that rename produces Same
  1548  	snap = updateProgramWithResource(snap, []Resource{{
  1549  		t:       "pkgA:index:t1",
  1550  		name:    "n2",
  1551  		aliases: []resource.Alias{{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"}},
  1552  	}}, []display.StepOp{deploy.OpSame}, false)
  1553  
  1554  	// Ensure that rename produces Same with multiple aliases
  1555  	snap = updateProgramWithResource(snap, []Resource{{
  1556  		t:    "pkgA:index:t1",
  1557  		name: "n3",
  1558  		aliases: []resource.Alias{
  1559  			{Name: "n2", Type: "pkgA:index:t1", Stack: "test", Project: "test"},
  1560  			{Name: "n1", Type: "pkgA:index:t1", Stack: "test", Project: "test"},
  1561  		},
  1562  	}}, []display.StepOp{deploy.OpSame}, false)
  1563  
  1564  	// Ensure that rename produces Same with multiple aliases (reversed)
  1565  	snap = updateProgramWithResource(snap, []Resource{{
  1566  		t:    "pkgA:index:t1",
  1567  		name: "n3",
  1568  		aliases: []resource.Alias{
  1569  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n2"},
  1570  			{Name: "n1", Type: "pkgA:index:t1", Stack: "test", Project: "test"},
  1571  		},
  1572  	}}, []display.StepOp{deploy.OpSame}, false)
  1573  
  1574  	// Ensure that aliasing back to original name is okay
  1575  	snap = updateProgramWithResource(snap, []Resource{{
  1576  		t:    "pkgA:index:t1",
  1577  		name: "n1",
  1578  		aliases: []resource.Alias{
  1579  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n3"},
  1580  			{Name: "n2", Type: "pkgA:index:t1", Stack: "test", Project: "test"},
  1581  		},
  1582  		aliasURNs: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1::n3"},
  1583  	}}, []display.StepOp{deploy.OpSame}, false)
  1584  
  1585  	// Ensure that removing aliases is okay (once old names are gone from all snapshots)
  1586  	snap = updateProgramWithResource(snap, []Resource{{
  1587  		t:    "pkgA:index:t1",
  1588  		name: "n1",
  1589  	}}, []display.StepOp{deploy.OpSame}, false)
  1590  
  1591  	// Ensure that changing the type works
  1592  	snap = updateProgramWithResource(snap, []Resource{{
  1593  		t:    "pkgA:index:t2",
  1594  		name: "n1",
  1595  		aliases: []resource.Alias{
  1596  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1597  		},
  1598  	}}, []display.StepOp{deploy.OpSame}, false)
  1599  
  1600  	// Ensure that changing the type again works
  1601  	snap = updateProgramWithResource(snap, []Resource{{
  1602  		t:    "pkgA:othermod:t3",
  1603  		name: "n1",
  1604  		aliases: []resource.Alias{
  1605  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1606  			{URN: "urn:pulumi:test::test::pkgA:index:t2::n1"},
  1607  		},
  1608  	}}, []display.StepOp{deploy.OpSame}, false)
  1609  
  1610  	// Ensure that order of aliases doesn't matter
  1611  	snap = updateProgramWithResource(snap, []Resource{{
  1612  		t:    "pkgA:othermod:t3",
  1613  		name: "n1",
  1614  		aliases: []resource.Alias{
  1615  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1616  			{URN: "urn:pulumi:test::test::pkgA:othermod:t3::n1"},
  1617  			{URN: "urn:pulumi:test::test::pkgA:index:t2::n1"},
  1618  		},
  1619  	}}, []display.StepOp{deploy.OpSame}, false)
  1620  
  1621  	// Ensure that removing aliases is okay (once old names are gone from all snapshots)
  1622  	snap = updateProgramWithResource(snap, []Resource{{
  1623  		t:    "pkgA:othermod:t3",
  1624  		name: "n1",
  1625  	}}, []display.StepOp{deploy.OpSame}, false)
  1626  
  1627  	// Ensure that changing everything (including props) leads to update not delete and re-create
  1628  	snap = updateProgramWithResource(snap, []Resource{{
  1629  		t:    "pkgA:index:t4",
  1630  		name: "n2",
  1631  		props: resource.PropertyMap{
  1632  			resource.PropertyKey("x"): resource.NewNumberProperty(42),
  1633  		},
  1634  		aliases: []resource.Alias{
  1635  			{URN: "urn:pulumi:test::test::pkgA:othermod:t3::n1"},
  1636  		},
  1637  	}}, []display.StepOp{deploy.OpUpdate}, false)
  1638  
  1639  	// Ensure that changing everything again (including props) leads to update not delete and re-create
  1640  	snap = updateProgramWithResource(snap, []Resource{{
  1641  		t:    "pkgA:index:t5",
  1642  		name: "n3",
  1643  		props: resource.PropertyMap{
  1644  			resource.PropertyKey("x"): resource.NewNumberProperty(1000),
  1645  		},
  1646  		aliases: []resource.Alias{
  1647  			{URN: "urn:pulumi:test::test::pkgA:index:t4::n2"},
  1648  		},
  1649  	}}, []display.StepOp{deploy.OpUpdate}, false)
  1650  
  1651  	// Ensure that changing a forceNew property while also changing type and name leads to replacement not delete+create
  1652  	snap = updateProgramWithResource(snap, []Resource{{
  1653  		t:    "pkgA:index:t6",
  1654  		name: "n4",
  1655  		props: resource.PropertyMap{
  1656  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1000),
  1657  		},
  1658  		aliases: []resource.Alias{
  1659  			{URN: "urn:pulumi:test::test::pkgA:index:t5::n3"},
  1660  		},
  1661  	}}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  1662  
  1663  	// Ensure that changing a forceNew property and deleteBeforeReplace while also changing type and name leads to
  1664  	// replacement not delete+create
  1665  	_ = updateProgramWithResource(snap, []Resource{{
  1666  		t:    "pkgA:index:t7",
  1667  		name: "n5",
  1668  		props: resource.PropertyMap{
  1669  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(999),
  1670  		},
  1671  		deleteBeforeReplace: true,
  1672  		aliases: []resource.Alias{
  1673  			{URN: "urn:pulumi:test::test::pkgA:index:t6::n4"},
  1674  		},
  1675  	}}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  1676  
  1677  	// Start again - this time with two resources with depends on relationship
  1678  	snap = updateProgramWithResource(nil, []Resource{{
  1679  		t:    "pkgA:index:t1",
  1680  		name: "n1",
  1681  		props: resource.PropertyMap{
  1682  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1),
  1683  		},
  1684  		deleteBeforeReplace: true,
  1685  	}, {
  1686  		t:            "pkgA:index:t2",
  1687  		name:         "n2",
  1688  		dependencies: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1::n1"},
  1689  	}}, []display.StepOp{deploy.OpCreate}, false)
  1690  
  1691  	_ = updateProgramWithResource(snap, []Resource{{
  1692  		t:    "pkgA:index:t1-new",
  1693  		name: "n1-new",
  1694  		props: resource.PropertyMap{
  1695  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(2),
  1696  		},
  1697  		deleteBeforeReplace: true,
  1698  		aliases: []resource.Alias{
  1699  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1700  		},
  1701  	}, {
  1702  		t:            "pkgA:index:t2-new",
  1703  		name:         "n2-new",
  1704  		dependencies: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1-new::n1-new"},
  1705  		aliases: []resource.Alias{
  1706  			{URN: "urn:pulumi:test::test::pkgA:index:t2::n2"},
  1707  		},
  1708  	}}, []display.StepOp{deploy.OpSame, deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  1709  
  1710  	// Start again - this time with two resources with parent relationship
  1711  	snap = updateProgramWithResource(nil, []Resource{{
  1712  		t:    "pkgA:index:t1",
  1713  		name: "n1",
  1714  		props: resource.PropertyMap{
  1715  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1),
  1716  		},
  1717  		deleteBeforeReplace: true,
  1718  	}, {
  1719  		t:      "pkgA:index:t2",
  1720  		name:   "n2",
  1721  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1::n1"),
  1722  	}}, []display.StepOp{deploy.OpCreate}, false)
  1723  
  1724  	_ = updateProgramWithResource(snap, []Resource{{
  1725  		t:    "pkgA:index:t1-new",
  1726  		name: "n1-new",
  1727  		props: resource.PropertyMap{
  1728  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(2),
  1729  		},
  1730  		deleteBeforeReplace: true,
  1731  		aliases: []resource.Alias{
  1732  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1733  		},
  1734  	}, {
  1735  		t:      "pkgA:index:t2-new",
  1736  		name:   "n2-new",
  1737  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new::n1-new"),
  1738  		aliases: []resource.Alias{
  1739  			{URN: "urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n2"},
  1740  		},
  1741  	}}, []display.StepOp{deploy.OpSame, deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  1742  
  1743  	// ensure failure when different resources use duplicate aliases
  1744  	_ = updateProgramWithResource(snap, []Resource{{
  1745  		t:    "pkgA:index:t1",
  1746  		name: "n2",
  1747  		aliases: []resource.Alias{
  1748  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1749  		},
  1750  	}, {
  1751  		t:    "pkgA:index:t2",
  1752  		name: "n3",
  1753  		aliases: []resource.Alias{
  1754  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1755  		},
  1756  	}}, []display.StepOp{deploy.OpCreate}, true)
  1757  
  1758  	// ensure different resources can use different aliases
  1759  	_ = updateProgramWithResource(nil, []Resource{{
  1760  		t:    "pkgA:index:t1",
  1761  		name: "n1",
  1762  		aliases: []resource.Alias{
  1763  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1764  		},
  1765  	}, {
  1766  		t:    "pkgA:index:t2",
  1767  		name: "n2",
  1768  		aliases: []resource.Alias{
  1769  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n2"},
  1770  		},
  1771  	}}, []display.StepOp{deploy.OpCreate}, false)
  1772  
  1773  	// ensure that aliases of parents of parents resolves correctly
  1774  	// first create a chain of resources such that we have n1 -> n1-sub -> n1-sub-sub
  1775  	snap = updateProgramWithResource(nil, []Resource{{
  1776  		t:    "pkgA:index:t1",
  1777  		name: "n1",
  1778  	}, {
  1779  		t:      "pkgA:index:t2",
  1780  		name:   "n1-sub",
  1781  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1::n1"),
  1782  	}, {
  1783  		t:      "pkgA:index:t3",
  1784  		name:   "n1-sub-sub",
  1785  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n1-sub"),
  1786  	}}, []display.StepOp{deploy.OpCreate}, false)
  1787  
  1788  	// Now change n1's name and type
  1789  	_ = updateProgramWithResource(snap, []Resource{{
  1790  		t:    "pkgA:index:t1-new",
  1791  		name: "n1-new",
  1792  		aliases: []resource.Alias{
  1793  			{URN: "urn:pulumi:test::test::pkgA:index:t1::n1"},
  1794  		},
  1795  	}, {
  1796  		t:      "pkgA:index:t2",
  1797  		name:   "n1-new-sub",
  1798  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new::n1-new"),
  1799  		aliases: []resource.Alias{
  1800  			{URN: "urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n1-sub"},
  1801  		},
  1802  	}, {
  1803  		t:      "pkgA:index:t3",
  1804  		name:   "n1-new-sub-sub",
  1805  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new$pkgA:index:t2::n1-new-sub"),
  1806  		aliases: []resource.Alias{
  1807  			{URN: "urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2$pkgA:index:t3::n1-sub-sub"},
  1808  		},
  1809  	}}, []display.StepOp{deploy.OpSame}, false)
  1810  
  1811  	// Test catastrophic multiplication out of aliases doesn't crash out of memory
  1812  	// first create a chain of resources such that we have n1 -> n1-sub -> n1-sub-sub
  1813  	snap = updateProgramWithResource(nil, []Resource{{
  1814  		t:    "pkgA:index:t1-v0",
  1815  		name: "n1",
  1816  	}, {
  1817  		t:      "pkgA:index:t2-v0",
  1818  		name:   "n1-sub",
  1819  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-v0::n1"),
  1820  	}, {
  1821  		t:      "pkgA:index:t3",
  1822  		name:   "n1-sub-sub",
  1823  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-v0$pkgA:index:t2-v0::n1-sub"),
  1824  	}}, []display.StepOp{deploy.OpCreate}, false)
  1825  
  1826  	// Now change n1's name and type and n2's type, but also add a load of aliases and pre-multiply them out
  1827  	// before sending to the engine
  1828  	n1Aliases := make([]resource.Alias, 0)
  1829  	n2Aliases := make([]resource.Alias, 0)
  1830  	n3Aliases := make([]resource.Alias, 0)
  1831  	for i := 0; i < 100; i++ {
  1832  		n1Aliases = append(n1Aliases, resource.Alias{URN: resource.URN(
  1833  			fmt.Sprintf("urn:pulumi:test::test::pkgA:index:t1-v%d::n1", i),
  1834  		)})
  1835  
  1836  		for j := 0; j < 10; j++ {
  1837  			n2Aliases = append(n2Aliases, resource.Alias{
  1838  				URN: resource.URN(fmt.Sprintf("urn:pulumi:test::test::pkgA:index:t1-v%d$pkgA:index:t2-v%d::n1-sub", i, j))})
  1839  			n3Aliases = append(n3Aliases, resource.Alias{
  1840  				Name:    "n1-sub-sub",
  1841  				Type:    fmt.Sprintf("pkgA:index:t1-v%d$pkgA:index:t2-v%d$pkgA:index:t3", i, j),
  1842  				Stack:   "test",
  1843  				Project: "test",
  1844  			})
  1845  		}
  1846  	}
  1847  
  1848  	snap = updateProgramWithResource(snap, []Resource{{
  1849  		t:       "pkgA:index:t1-v100",
  1850  		name:    "n1-new",
  1851  		aliases: n1Aliases,
  1852  	}, {
  1853  		t:       "pkgA:index:t2-v10",
  1854  		name:    "n1-new-sub",
  1855  		parent:  resource.URN("urn:pulumi:test::test::pkgA:index:t1-v100::n1-new"),
  1856  		aliases: n2Aliases,
  1857  	}, {
  1858  		t:       "pkgA:index:t3",
  1859  		name:    "n1-new-sub-sub",
  1860  		parent:  resource.URN("urn:pulumi:test::test::pkgA:index:t1-v100$pkgA:index:t2-v10::n1-new-sub"),
  1861  		aliases: n3Aliases,
  1862  	}}, []display.StepOp{deploy.OpSame}, false)
  1863  
  1864  	var err error
  1865  	_, err = snap.NormalizeURNReferences()
  1866  	assert.Nil(t, err)
  1867  }
  1868  
  1869  func TestAliasURNs(t *testing.T) {
  1870  	t.Parallel()
  1871  
  1872  	loaders := []*deploytest.ProviderLoader{
  1873  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  1874  			return &deploytest.Provider{
  1875  				// The `forcesReplacement` key forces replacement and all other keys can update in place
  1876  				DiffF: func(res resource.URN, id resource.ID, olds, news resource.PropertyMap,
  1877  					ignoreChanges []string) (plugin.DiffResult, error) {
  1878  
  1879  					replaceKeys := []resource.PropertyKey{}
  1880  					old, hasOld := olds["forcesReplacement"]
  1881  					new, hasNew := news["forcesReplacement"]
  1882  					if hasOld && !hasNew || hasNew && !hasOld || hasOld && hasNew && old.Diff(new) != nil {
  1883  						replaceKeys = append(replaceKeys, "forcesReplacement")
  1884  					}
  1885  					return plugin.DiffResult{ReplaceKeys: replaceKeys}, nil
  1886  				},
  1887  			}, nil
  1888  		}),
  1889  	}
  1890  
  1891  	updateProgramWithResource := func(
  1892  		snap *deploy.Snapshot, resources []Resource, allowedOps []display.StepOp, expectFailure bool) *deploy.Snapshot {
  1893  		program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  1894  			err := registerResources(t, monitor, resources)
  1895  			return err
  1896  		})
  1897  		host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  1898  		p := &TestPlan{
  1899  			Options: UpdateOptions{Host: host},
  1900  			Steps: []TestStep{
  1901  				{
  1902  					Op:            Update,
  1903  					ExpectFailure: expectFailure,
  1904  					Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  1905  						events []Event, res result.Result) result.Result {
  1906  						for _, event := range events {
  1907  							if event.Type == ResourcePreEvent {
  1908  								payload := event.Payload().(ResourcePreEventPayload)
  1909  								assert.Subset(t, allowedOps, []display.StepOp{payload.Metadata.Op})
  1910  							}
  1911  						}
  1912  
  1913  						for _, entry := range entries {
  1914  							if entry.Step.Type() == "pulumi:providers:pkgA" {
  1915  								continue
  1916  							}
  1917  							switch entry.Kind {
  1918  							case JournalEntrySuccess:
  1919  								assert.Subset(t, allowedOps, []display.StepOp{entry.Step.Op()})
  1920  							case JournalEntryFailure:
  1921  								assert.Fail(t, "unexpected failure in journal")
  1922  							case JournalEntryBegin:
  1923  							case JournalEntryOutputs:
  1924  							}
  1925  						}
  1926  
  1927  						return res
  1928  					},
  1929  				},
  1930  			},
  1931  		}
  1932  		return p.Run(t, snap)
  1933  	}
  1934  
  1935  	snap := updateProgramWithResource(nil, []Resource{{
  1936  		t:    "pkgA:index:t1",
  1937  		name: "n1",
  1938  	}}, []display.StepOp{deploy.OpCreate}, false)
  1939  
  1940  	// Ensure that rename produces Same
  1941  	snap = updateProgramWithResource(snap, []Resource{{
  1942  		t:         "pkgA:index:t1",
  1943  		name:      "n2",
  1944  		aliasURNs: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1::n1"},
  1945  	}}, []display.StepOp{deploy.OpSame}, false)
  1946  
  1947  	// Ensure that rename produces Same with multiple aliases
  1948  	snap = updateProgramWithResource(snap, []Resource{{
  1949  		t:    "pkgA:index:t1",
  1950  		name: "n3",
  1951  		aliasURNs: []resource.URN{
  1952  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  1953  			"urn:pulumi:test::test::pkgA:index:t1::n2",
  1954  		},
  1955  	}}, []display.StepOp{deploy.OpSame}, false)
  1956  
  1957  	// Ensure that rename produces Same with multiple aliases (reversed)
  1958  	snap = updateProgramWithResource(snap, []Resource{{
  1959  		t:    "pkgA:index:t1",
  1960  		name: "n3",
  1961  		aliasURNs: []resource.URN{
  1962  			"urn:pulumi:test::test::pkgA:index:t1::n2",
  1963  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  1964  		},
  1965  	}}, []display.StepOp{deploy.OpSame}, false)
  1966  
  1967  	// Ensure that aliasing back to original name is okay
  1968  	snap = updateProgramWithResource(snap, []Resource{{
  1969  		t:    "pkgA:index:t1",
  1970  		name: "n1",
  1971  		aliasURNs: []resource.URN{
  1972  			"urn:pulumi:test::test::pkgA:index:t1::n3",
  1973  			"urn:pulumi:test::test::pkgA:index:t1::n2",
  1974  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  1975  		},
  1976  	}}, []display.StepOp{deploy.OpSame}, false)
  1977  
  1978  	// Ensure that removing aliases is okay (once old names are gone from all snapshots)
  1979  	snap = updateProgramWithResource(snap, []Resource{{
  1980  		t:    "pkgA:index:t1",
  1981  		name: "n1",
  1982  	}}, []display.StepOp{deploy.OpSame}, false)
  1983  
  1984  	// Ensure that changing the type works
  1985  	snap = updateProgramWithResource(snap, []Resource{{
  1986  		t:    "pkgA:index:t2",
  1987  		name: "n1",
  1988  		aliasURNs: []resource.URN{
  1989  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  1990  		},
  1991  	}}, []display.StepOp{deploy.OpSame}, false)
  1992  
  1993  	// Ensure that changing the type again works
  1994  	snap = updateProgramWithResource(snap, []Resource{{
  1995  		t:    "pkgA:othermod:t3",
  1996  		name: "n1",
  1997  		aliasURNs: []resource.URN{
  1998  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  1999  			"urn:pulumi:test::test::pkgA:index:t2::n1",
  2000  		},
  2001  	}}, []display.StepOp{deploy.OpSame}, false)
  2002  
  2003  	// Ensure that order of aliases doesn't matter
  2004  	snap = updateProgramWithResource(snap, []Resource{{
  2005  		t:    "pkgA:othermod:t3",
  2006  		name: "n1",
  2007  		aliasURNs: []resource.URN{
  2008  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2009  			"urn:pulumi:test::test::pkgA:othermod:t3::n1",
  2010  			"urn:pulumi:test::test::pkgA:index:t2::n1",
  2011  		},
  2012  	}}, []display.StepOp{deploy.OpSame}, false)
  2013  
  2014  	// Ensure that removing aliases is okay (once old names are gone from all snapshots)
  2015  	snap = updateProgramWithResource(snap, []Resource{{
  2016  		t:    "pkgA:othermod:t3",
  2017  		name: "n1",
  2018  	}}, []display.StepOp{deploy.OpSame}, false)
  2019  
  2020  	// Ensure that changing everything (including props) leads to update not delete and re-create
  2021  	snap = updateProgramWithResource(snap, []Resource{{
  2022  		t:    "pkgA:index:t4",
  2023  		name: "n2",
  2024  		props: resource.PropertyMap{
  2025  			resource.PropertyKey("x"): resource.NewNumberProperty(42),
  2026  		},
  2027  		aliasURNs: []resource.URN{
  2028  			"urn:pulumi:test::test::pkgA:othermod:t3::n1",
  2029  		},
  2030  	}}, []display.StepOp{deploy.OpUpdate}, false)
  2031  
  2032  	// Ensure that changing everything again (including props) leads to update not delete and re-create
  2033  	snap = updateProgramWithResource(snap, []Resource{{
  2034  		t:    "pkgA:index:t5",
  2035  		name: "n3",
  2036  		props: resource.PropertyMap{
  2037  			resource.PropertyKey("x"): resource.NewNumberProperty(1000),
  2038  		},
  2039  		aliasURNs: []resource.URN{
  2040  			"urn:pulumi:test::test::pkgA:index:t4::n2",
  2041  		},
  2042  	}}, []display.StepOp{deploy.OpUpdate}, false)
  2043  
  2044  	// Ensure that changing a forceNew property while also changing type and name leads to replacement not delete+create
  2045  	snap = updateProgramWithResource(snap, []Resource{{
  2046  		t:    "pkgA:index:t6",
  2047  		name: "n4",
  2048  		props: resource.PropertyMap{
  2049  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1000),
  2050  		},
  2051  		aliasURNs: []resource.URN{
  2052  			"urn:pulumi:test::test::pkgA:index:t5::n3",
  2053  		},
  2054  	}}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  2055  
  2056  	// Ensure that changing a forceNew property and deleteBeforeReplace while also changing type and name leads to
  2057  	// replacement not delete+create
  2058  	_ = updateProgramWithResource(snap, []Resource{{
  2059  		t:    "pkgA:index:t7",
  2060  		name: "n5",
  2061  		props: resource.PropertyMap{
  2062  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(999),
  2063  		},
  2064  		deleteBeforeReplace: true,
  2065  		aliasURNs: []resource.URN{
  2066  			"urn:pulumi:test::test::pkgA:index:t6::n4",
  2067  		},
  2068  	}}, []display.StepOp{deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  2069  
  2070  	// Start again - this time with two resources with depends on relationship
  2071  	snap = updateProgramWithResource(nil, []Resource{{
  2072  		t:    "pkgA:index:t1",
  2073  		name: "n1",
  2074  		props: resource.PropertyMap{
  2075  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1),
  2076  		},
  2077  		deleteBeforeReplace: true,
  2078  	}, {
  2079  		t:            "pkgA:index:t2",
  2080  		name:         "n2",
  2081  		dependencies: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1::n1"},
  2082  	}}, []display.StepOp{deploy.OpCreate}, false)
  2083  
  2084  	_ = updateProgramWithResource(snap, []Resource{{
  2085  		t:    "pkgA:index:t1-new",
  2086  		name: "n1-new",
  2087  		props: resource.PropertyMap{
  2088  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(2),
  2089  		},
  2090  		deleteBeforeReplace: true,
  2091  		aliasURNs: []resource.URN{
  2092  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2093  		},
  2094  	}, {
  2095  		t:            "pkgA:index:t2-new",
  2096  		name:         "n2-new",
  2097  		dependencies: []resource.URN{"urn:pulumi:test::test::pkgA:index:t1-new::n1-new"},
  2098  		aliasURNs: []resource.URN{
  2099  			"urn:pulumi:test::test::pkgA:index:t2::n2",
  2100  		},
  2101  	}}, []display.StepOp{deploy.OpSame, deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  2102  
  2103  	// Start again - this time with two resources with parent relationship
  2104  	snap = updateProgramWithResource(nil, []Resource{{
  2105  		t:    "pkgA:index:t1",
  2106  		name: "n1",
  2107  		props: resource.PropertyMap{
  2108  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(1),
  2109  		},
  2110  		deleteBeforeReplace: true,
  2111  	}, {
  2112  		t:      "pkgA:index:t2",
  2113  		name:   "n2",
  2114  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1::n1"),
  2115  	}}, []display.StepOp{deploy.OpCreate}, false)
  2116  
  2117  	_ = updateProgramWithResource(snap, []Resource{{
  2118  		t:    "pkgA:index:t1-new",
  2119  		name: "n1-new",
  2120  		props: resource.PropertyMap{
  2121  			resource.PropertyKey("forcesReplacement"): resource.NewNumberProperty(2),
  2122  		},
  2123  		deleteBeforeReplace: true,
  2124  		aliasURNs: []resource.URN{
  2125  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2126  		},
  2127  	}, {
  2128  		t:      "pkgA:index:t2-new",
  2129  		name:   "n2-new",
  2130  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new::n1-new"),
  2131  		aliasURNs: []resource.URN{
  2132  			"urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n2",
  2133  		},
  2134  	}}, []display.StepOp{deploy.OpSame, deploy.OpReplace, deploy.OpCreateReplacement, deploy.OpDeleteReplaced}, false)
  2135  
  2136  	// ensure failure when different resources use duplicate aliases
  2137  	_ = updateProgramWithResource(snap, []Resource{{
  2138  		t:    "pkgA:index:t1",
  2139  		name: "n2",
  2140  		aliasURNs: []resource.URN{
  2141  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2142  		},
  2143  	}, {
  2144  		t:    "pkgA:index:t2",
  2145  		name: "n3",
  2146  		aliasURNs: []resource.URN{
  2147  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2148  		},
  2149  	}}, []display.StepOp{deploy.OpCreate}, true)
  2150  
  2151  	// ensure different resources can use different aliases
  2152  	_ = updateProgramWithResource(nil, []Resource{{
  2153  		t:    "pkgA:index:t1",
  2154  		name: "n1",
  2155  		aliasURNs: []resource.URN{
  2156  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2157  		},
  2158  	}, {
  2159  		t:    "pkgA:index:t2",
  2160  		name: "n2",
  2161  		aliasURNs: []resource.URN{
  2162  			"urn:pulumi:test::test::pkgA:index:t1::n2",
  2163  		},
  2164  	}}, []display.StepOp{deploy.OpCreate}, false)
  2165  
  2166  	// ensure that aliases of parents of parents resolves correctly
  2167  	// first create a chain of resources such that we have n1 -> n1-sub -> n1-sub-sub
  2168  	snap = updateProgramWithResource(nil, []Resource{{
  2169  		t:    "pkgA:index:t1",
  2170  		name: "n1",
  2171  	}, {
  2172  		t:      "pkgA:index:t2",
  2173  		name:   "n1-sub",
  2174  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1::n1"),
  2175  	}, {
  2176  		t:      "pkgA:index:t3",
  2177  		name:   "n1-sub-sub",
  2178  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n1-sub"),
  2179  	}}, []display.StepOp{deploy.OpCreate}, false)
  2180  
  2181  	// Now change n1's name and type
  2182  	_ = updateProgramWithResource(snap, []Resource{{
  2183  		t:    "pkgA:index:t1-new",
  2184  		name: "n1-new",
  2185  		aliasURNs: []resource.URN{
  2186  			"urn:pulumi:test::test::pkgA:index:t1::n1",
  2187  		},
  2188  	}, {
  2189  		t:      "pkgA:index:t2",
  2190  		name:   "n1-new-sub",
  2191  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new::n1-new"),
  2192  		aliasURNs: []resource.URN{
  2193  			"urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2::n1-sub",
  2194  		},
  2195  	}, {
  2196  		t:      "pkgA:index:t3",
  2197  		name:   "n1-new-sub-sub",
  2198  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-new$pkgA:index:t2::n1-new-sub"),
  2199  		aliasURNs: []resource.URN{
  2200  			"urn:pulumi:test::test::pkgA:index:t1$pkgA:index:t2$pkgA:index:t3::n1-sub-sub",
  2201  		},
  2202  	}}, []display.StepOp{deploy.OpSame}, false)
  2203  
  2204  	// Test catastrophic multiplication out of aliases doesn't crash out of memory
  2205  	// first create a chain of resources such that we have n1 -> n1-sub -> n1-sub-sub
  2206  	snap = updateProgramWithResource(nil, []Resource{{
  2207  		t:    "pkgA:index:t1-v0",
  2208  		name: "n1",
  2209  	}, {
  2210  		t:      "pkgA:index:t2-v0",
  2211  		name:   "n1-sub",
  2212  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-v0::n1"),
  2213  	}, {
  2214  		t:      "pkgA:index:t3",
  2215  		name:   "n1-sub-sub",
  2216  		parent: resource.URN("urn:pulumi:test::test::pkgA:index:t1-v0$pkgA:index:t2-v0::n1-sub"),
  2217  	}}, []display.StepOp{deploy.OpCreate}, false)
  2218  
  2219  	// Now change n1's name and type and n2's type, but also add a load of aliases and pre-multiply them out
  2220  	// before sending to the engine
  2221  	n1Aliases := make([]resource.URN, 0)
  2222  	n2Aliases := make([]resource.URN, 0)
  2223  	n3Aliases := make([]resource.URN, 0)
  2224  	for i := 0; i < 100; i++ {
  2225  		n1Aliases = append(n1Aliases, resource.URN(
  2226  			fmt.Sprintf("urn:pulumi:test::test::pkgA:index:t1-v%d::n1", i)))
  2227  
  2228  		for j := 0; j < 10; j++ {
  2229  			n2Aliases = append(n2Aliases, resource.URN(
  2230  				fmt.Sprintf("urn:pulumi:test::test::pkgA:index:t1-v%d$pkgA:index:t2-v%d::n1-sub", i, j)))
  2231  
  2232  			n3Aliases = append(n3Aliases, resource.URN(
  2233  				fmt.Sprintf("urn:pulumi:test::test::pkgA:index:t1-v%d$pkgA:index:t2-v%d$pkgA:index:t3::n1-sub-sub", i, j)))
  2234  		}
  2235  	}
  2236  
  2237  	snap = updateProgramWithResource(snap, []Resource{{
  2238  		t:         "pkgA:index:t1-v100",
  2239  		name:      "n1-new",
  2240  		aliasURNs: n1Aliases,
  2241  	}, {
  2242  		t:         "pkgA:index:t2-v10",
  2243  		name:      "n1-new-sub",
  2244  		parent:    resource.URN("urn:pulumi:test::test::pkgA:index:t1-v100::n1-new"),
  2245  		aliasURNs: n2Aliases,
  2246  	}, {
  2247  		t:         "pkgA:index:t3",
  2248  		name:      "n1-new-sub-sub",
  2249  		parent:    resource.URN("urn:pulumi:test::test::pkgA:index:t1-v100$pkgA:index:t2-v10::n1-new-sub"),
  2250  		aliasURNs: n3Aliases,
  2251  	}}, []display.StepOp{deploy.OpSame}, false)
  2252  
  2253  	var err error
  2254  	_, err = snap.NormalizeURNReferences()
  2255  	assert.Nil(t, err)
  2256  }
  2257  
  2258  func TestPersistentDiff(t *testing.T) {
  2259  	t.Parallel()
  2260  
  2261  	loaders := []*deploytest.ProviderLoader{
  2262  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2263  			return &deploytest.Provider{
  2264  				DiffF: func(urn resource.URN, id resource.ID,
  2265  					olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  2266  
  2267  					return plugin.DiffResult{Changes: plugin.DiffSome}, nil
  2268  				},
  2269  			}, nil
  2270  		}),
  2271  	}
  2272  
  2273  	inputs := resource.PropertyMap{}
  2274  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2275  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2276  			Inputs: inputs,
  2277  		})
  2278  		assert.NoError(t, err)
  2279  		return nil
  2280  	})
  2281  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2282  
  2283  	p := &TestPlan{
  2284  		Options: UpdateOptions{Host: host},
  2285  	}
  2286  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
  2287  
  2288  	// Run the initial update.
  2289  	project := p.GetProject()
  2290  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  2291  	assert.Nil(t, res)
  2292  
  2293  	// First, make no change to the inputs and run a preview. We should see an update to the resource due to
  2294  	// provider diffing.
  2295  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, true, p.BackendClient,
  2296  		func(_ workspace.Project, _ deploy.Target, _ JournalEntries,
  2297  			events []Event, res result.Result) result.Result {
  2298  
  2299  			found := false
  2300  			for _, e := range events {
  2301  				if e.Type == ResourcePreEvent {
  2302  					p := e.Payload().(ResourcePreEventPayload).Metadata
  2303  					if p.URN == resURN {
  2304  						assert.Equal(t, deploy.OpUpdate, p.Op)
  2305  						found = true
  2306  					}
  2307  				}
  2308  			}
  2309  			assert.True(t, found)
  2310  			return res
  2311  		})
  2312  	assert.Nil(t, res)
  2313  
  2314  	// Next, enable legacy diff behavior. We should see no changes to the resource.
  2315  	p.Options.UseLegacyDiff = true
  2316  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, true, p.BackendClient,
  2317  		func(_ workspace.Project, _ deploy.Target, _ JournalEntries,
  2318  			events []Event, res result.Result) result.Result {
  2319  
  2320  			found := false
  2321  			for _, e := range events {
  2322  				if e.Type == ResourcePreEvent {
  2323  					p := e.Payload().(ResourcePreEventPayload).Metadata
  2324  					if p.URN == resURN {
  2325  						assert.Equal(t, deploy.OpSame, p.Op)
  2326  						found = true
  2327  					}
  2328  				}
  2329  			}
  2330  			assert.True(t, found)
  2331  			return res
  2332  		})
  2333  	assert.Nil(t, res)
  2334  }
  2335  
  2336  func TestDetailedDiffReplace(t *testing.T) {
  2337  	t.Parallel()
  2338  
  2339  	loaders := []*deploytest.ProviderLoader{
  2340  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2341  			return &deploytest.Provider{
  2342  				DiffF: func(urn resource.URN, id resource.ID,
  2343  					olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  2344  
  2345  					return plugin.DiffResult{
  2346  						Changes: plugin.DiffSome,
  2347  						DetailedDiff: map[string]plugin.PropertyDiff{
  2348  							"prop": {Kind: plugin.DiffAddReplace},
  2349  						},
  2350  					}, nil
  2351  				},
  2352  			}, nil
  2353  		}),
  2354  	}
  2355  
  2356  	inputs := resource.PropertyMap{}
  2357  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2358  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2359  			Inputs: inputs,
  2360  		})
  2361  		assert.NoError(t, err)
  2362  		return nil
  2363  	})
  2364  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2365  
  2366  	p := &TestPlan{
  2367  		Options: UpdateOptions{Host: host},
  2368  	}
  2369  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
  2370  
  2371  	// Run the initial update.
  2372  	project := p.GetProject()
  2373  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  2374  	assert.Nil(t, res)
  2375  
  2376  	// First, make no change to the inputs and run a preview. We should see an update to the resource due to
  2377  	// provider diffing.
  2378  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, true, p.BackendClient,
  2379  		func(_ workspace.Project, _ deploy.Target, _ JournalEntries,
  2380  			events []Event, res result.Result) result.Result {
  2381  
  2382  			found := false
  2383  			for _, e := range events {
  2384  				if e.Type == ResourcePreEvent {
  2385  					p := e.Payload().(ResourcePreEventPayload).Metadata
  2386  					if p.URN == resURN && p.Op == deploy.OpReplace {
  2387  						found = true
  2388  					}
  2389  				}
  2390  			}
  2391  			assert.True(t, found)
  2392  			return res
  2393  		})
  2394  	assert.Nil(t, res)
  2395  }
  2396  
  2397  func TestCustomTimeouts(t *testing.T) {
  2398  	t.Parallel()
  2399  
  2400  	loaders := []*deploytest.ProviderLoader{
  2401  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2402  			return &deploytest.Provider{}, nil
  2403  		}),
  2404  	}
  2405  
  2406  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2407  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2408  			CustomTimeouts: &resource.CustomTimeouts{
  2409  				Create: 60, Delete: 60, Update: 240,
  2410  			},
  2411  		})
  2412  		assert.NoError(t, err)
  2413  		return nil
  2414  	})
  2415  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2416  
  2417  	p := &TestPlan{
  2418  		Options: UpdateOptions{Host: host},
  2419  	}
  2420  
  2421  	p.Steps = []TestStep{{Op: Update}}
  2422  	snap := p.Run(t, nil)
  2423  
  2424  	assert.Len(t, snap.Resources, 2)
  2425  	assert.Equal(t, string(snap.Resources[0].URN.Name()), "default")
  2426  	assert.Equal(t, string(snap.Resources[1].URN.Name()), "resA")
  2427  	assert.NotNil(t, snap.Resources[1].CustomTimeouts)
  2428  	assert.Equal(t, snap.Resources[1].CustomTimeouts.Create, float64(60))
  2429  	assert.Equal(t, snap.Resources[1].CustomTimeouts.Update, float64(240))
  2430  	assert.Equal(t, snap.Resources[1].CustomTimeouts.Delete, float64(60))
  2431  }
  2432  
  2433  func TestProviderDiffMissingOldOutputs(t *testing.T) {
  2434  	t.Parallel()
  2435  
  2436  	loaders := []*deploytest.ProviderLoader{
  2437  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2438  			return &deploytest.Provider{
  2439  				DiffConfigF: func(urn resource.URN, olds, news resource.PropertyMap,
  2440  					ignoreChanges []string) (plugin.DiffResult, error) {
  2441  					// Always require replacement if any diff exists.
  2442  					if !olds.DeepEquals(news) {
  2443  						keys := []resource.PropertyKey{}
  2444  						for k := range news {
  2445  							keys = append(keys, k)
  2446  						}
  2447  						return plugin.DiffResult{Changes: plugin.DiffSome, ReplaceKeys: keys}, nil
  2448  					}
  2449  					return plugin.DiffResult{Changes: plugin.DiffNone}, nil
  2450  				},
  2451  			}, nil
  2452  		}),
  2453  	}
  2454  
  2455  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2456  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
  2457  		assert.NoError(t, err)
  2458  		return nil
  2459  	})
  2460  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2461  
  2462  	p := &TestPlan{
  2463  		Options: UpdateOptions{Host: host},
  2464  		Config: config.Map{
  2465  			config.MustMakeKey("pkgA", "foo"): config.NewValue("bar"),
  2466  		},
  2467  	}
  2468  
  2469  	// Build a basic lifecycle.
  2470  	steps := MakeBasicLifecycleSteps(t, 2)
  2471  
  2472  	// Run the lifecycle through its initial update and refresh.
  2473  	p.Steps = steps[:2]
  2474  	snap := p.Run(t, nil)
  2475  
  2476  	// Delete the old provider outputs (if any) from the checkpoint, then run the no-op update.
  2477  	providerURN := p.NewProviderURN("pkgA", "default", "")
  2478  	for _, r := range snap.Resources {
  2479  		if r.URN == providerURN {
  2480  			r.Outputs = nil
  2481  		}
  2482  	}
  2483  
  2484  	p.Steps = steps[2:3]
  2485  	snap = p.Run(t, snap)
  2486  
  2487  	// Change the config, delete the old provider outputs,  and run an update. We expect everything to require
  2488  	// replacement.
  2489  	p.Config[config.MustMakeKey("pkgA", "foo")] = config.NewValue("baz")
  2490  	for _, r := range snap.Resources {
  2491  		if r.URN == providerURN {
  2492  			r.Outputs = nil
  2493  		}
  2494  	}
  2495  	p.Steps = []TestStep{{
  2496  		Op: Update,
  2497  		Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  2498  			_ []Event, res result.Result) result.Result {
  2499  
  2500  			resURN := p.NewURN("pkgA:m:typA", "resA", "")
  2501  
  2502  			// Look for replace steps on the provider and the resource.
  2503  			replacedProvider, replacedResource := false, false
  2504  			for _, entry := range entries {
  2505  				if entry.Kind != JournalEntrySuccess || entry.Step.Op() != deploy.OpDeleteReplaced {
  2506  					continue
  2507  				}
  2508  
  2509  				switch urn := entry.Step.URN(); urn {
  2510  				case providerURN:
  2511  					replacedProvider = true
  2512  				case resURN:
  2513  					replacedResource = true
  2514  				default:
  2515  					t.Fatalf("unexpected resource %v", urn)
  2516  				}
  2517  			}
  2518  			assert.True(t, replacedProvider)
  2519  			assert.True(t, replacedResource)
  2520  
  2521  			return res
  2522  		},
  2523  	}}
  2524  	p.Run(t, snap)
  2525  }
  2526  
  2527  func TestMissingRead(t *testing.T) {
  2528  	t.Parallel()
  2529  
  2530  	loaders := []*deploytest.ProviderLoader{
  2531  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2532  			return &deploytest.Provider{
  2533  				ReadF: func(_ resource.URN, _ resource.ID, _, _ resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
  2534  					return plugin.ReadResult{}, resource.StatusOK, nil
  2535  				},
  2536  			}, nil
  2537  		}),
  2538  	}
  2539  
  2540  	// Our program reads a resource and exits.
  2541  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2542  		_, _, err := monitor.ReadResource("pkgA:m:typA", "resA", "resA-some-id", "", resource.PropertyMap{}, "", "")
  2543  		assert.Error(t, err)
  2544  		return nil
  2545  	})
  2546  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2547  	p := &TestPlan{
  2548  		Options: UpdateOptions{Host: host},
  2549  		Steps:   []TestStep{{Op: Update, ExpectFailure: true}},
  2550  	}
  2551  	p.Run(t, nil)
  2552  }
  2553  
  2554  func TestProviderPreview(t *testing.T) {
  2555  	t.Parallel()
  2556  
  2557  	sawPreview := false
  2558  	loaders := []*deploytest.ProviderLoader{
  2559  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2560  			return &deploytest.Provider{
  2561  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  2562  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  2563  
  2564  					if preview {
  2565  						sawPreview = true
  2566  					}
  2567  
  2568  					assert.Equal(t, preview, news.ContainsUnknowns())
  2569  					return "created-id", news, resource.StatusOK, nil
  2570  				},
  2571  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  2572  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  2573  
  2574  					if preview {
  2575  						sawPreview = true
  2576  					}
  2577  
  2578  					assert.Equal(t, preview, news.ContainsUnknowns())
  2579  					return news, resource.StatusOK, nil
  2580  				},
  2581  			}, nil
  2582  		}),
  2583  	}
  2584  
  2585  	preview := true
  2586  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2587  		computed := interface{}(resource.Computed{Element: resource.NewStringProperty("")})
  2588  		if !preview {
  2589  			computed = "alpha"
  2590  		}
  2591  
  2592  		ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  2593  			"foo": "bar",
  2594  			"baz": map[string]interface{}{
  2595  				"a": 42,
  2596  				"b": computed,
  2597  			},
  2598  			"qux": []interface{}{
  2599  				computed,
  2600  				24,
  2601  			},
  2602  			"zed": computed,
  2603  		})
  2604  
  2605  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2606  			Inputs: ins,
  2607  		})
  2608  		assert.NoError(t, err)
  2609  
  2610  		assert.True(t, state.DeepEquals(ins))
  2611  
  2612  		return nil
  2613  	})
  2614  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2615  
  2616  	p := &TestPlan{
  2617  		Options: UpdateOptions{Host: host},
  2618  	}
  2619  
  2620  	project := p.GetProject()
  2621  
  2622  	// Run a preview. The inputs should be propagated to the outputs by the provider during the create.
  2623  	preview, sawPreview = true, false
  2624  	_, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2625  	assert.Nil(t, res)
  2626  	assert.True(t, sawPreview)
  2627  
  2628  	// Run an update.
  2629  	preview, sawPreview = false, false
  2630  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2631  	assert.Nil(t, res)
  2632  	assert.False(t, sawPreview)
  2633  
  2634  	// Run another preview. The inputs should be propagated to the outputs during the update.
  2635  	preview, sawPreview = true, false
  2636  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, preview, p.BackendClient, nil)
  2637  	assert.Nil(t, res)
  2638  	assert.True(t, sawPreview)
  2639  }
  2640  
  2641  func TestProviderPreviewGrpc(t *testing.T) {
  2642  	t.Parallel()
  2643  
  2644  	sawPreview := false
  2645  	loaders := []*deploytest.ProviderLoader{
  2646  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2647  			return &deploytest.Provider{
  2648  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  2649  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  2650  
  2651  					if preview {
  2652  						sawPreview = true
  2653  					}
  2654  
  2655  					assert.Equal(t, preview, news.ContainsUnknowns())
  2656  					return "created-id", news, resource.StatusOK, nil
  2657  				},
  2658  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  2659  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  2660  
  2661  					if preview {
  2662  						sawPreview = true
  2663  					}
  2664  
  2665  					assert.Equal(t, preview, news.ContainsUnknowns())
  2666  					return news, resource.StatusOK, nil
  2667  				},
  2668  			}, nil
  2669  		}, deploytest.WithGrpc),
  2670  	}
  2671  
  2672  	preview := true
  2673  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2674  		computed := interface{}(resource.Computed{Element: resource.NewStringProperty("")})
  2675  		if !preview {
  2676  			computed = "alpha"
  2677  		}
  2678  
  2679  		ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  2680  			"foo": "bar",
  2681  			"baz": map[string]interface{}{
  2682  				"a": 42,
  2683  				"b": computed,
  2684  			},
  2685  			"qux": []interface{}{
  2686  				computed,
  2687  				24,
  2688  			},
  2689  			"zed": computed,
  2690  		})
  2691  
  2692  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2693  			Inputs: ins,
  2694  		})
  2695  		assert.NoError(t, err)
  2696  
  2697  		assert.True(t, state.DeepEquals(ins))
  2698  
  2699  		return nil
  2700  	})
  2701  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2702  
  2703  	p := &TestPlan{
  2704  		Options: UpdateOptions{Host: host},
  2705  	}
  2706  
  2707  	project := p.GetProject()
  2708  
  2709  	// Run a preview. The inputs should be propagated to the outputs by the provider during the create.
  2710  	preview, sawPreview = true, false
  2711  	_, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2712  	assert.Nil(t, res)
  2713  	assert.True(t, sawPreview)
  2714  
  2715  	// Run an update.
  2716  	preview, sawPreview = false, false
  2717  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2718  	assert.Nil(t, res)
  2719  	assert.False(t, sawPreview)
  2720  
  2721  	// Run another preview. The inputs should be propagated to the outputs during the update.
  2722  	preview, sawPreview = true, false
  2723  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, preview, p.BackendClient, nil)
  2724  	assert.Nil(t, res)
  2725  	assert.True(t, sawPreview)
  2726  }
  2727  
  2728  func TestProviderPreviewUnknowns(t *testing.T) {
  2729  	t.Parallel()
  2730  
  2731  	sawPreview := false
  2732  	loaders := []*deploytest.ProviderLoader{
  2733  		// NOTE: it is important that this test uses a gRPC-wraped provider. The code that handles previews for unconfigured
  2734  		// providers is specific to the gRPC layer.
  2735  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2736  			return &deploytest.Provider{
  2737  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  2738  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  2739  
  2740  					if preview {
  2741  						sawPreview = true
  2742  					}
  2743  
  2744  					return "created-id", news, resource.StatusOK, nil
  2745  				},
  2746  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  2747  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  2748  
  2749  					if preview {
  2750  						sawPreview = true
  2751  					}
  2752  
  2753  					return news, resource.StatusOK, nil
  2754  				},
  2755  			}, nil
  2756  		}, deploytest.WithGrpc),
  2757  	}
  2758  
  2759  	preview := true
  2760  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2761  		computed := interface{}(resource.Computed{Element: resource.NewStringProperty("")})
  2762  		if !preview {
  2763  			computed = "alpha"
  2764  		}
  2765  
  2766  		provURN, provID, _, err := monitor.RegisterResource("pulumi:providers:pkgA", "provA", true,
  2767  			deploytest.ResourceOptions{
  2768  				Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{"foo": computed}),
  2769  			})
  2770  		require.NoError(t, err)
  2771  
  2772  		ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  2773  			"foo": "bar",
  2774  			"baz": map[string]interface{}{
  2775  				"a": 42,
  2776  			},
  2777  			"qux": []interface{}{
  2778  				24,
  2779  			},
  2780  		})
  2781  
  2782  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  2783  			Inputs:   ins,
  2784  			Provider: fmt.Sprintf("%v::%v", provURN, provID),
  2785  		})
  2786  		require.NoError(t, err)
  2787  
  2788  		if preview {
  2789  			assert.True(t, state.DeepEquals(resource.PropertyMap{}))
  2790  		} else {
  2791  			assert.True(t, state.DeepEquals(ins))
  2792  		}
  2793  
  2794  		return nil
  2795  	})
  2796  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2797  
  2798  	p := &TestPlan{
  2799  		Options: UpdateOptions{Host: host},
  2800  	}
  2801  
  2802  	project := p.GetProject()
  2803  
  2804  	// Run a preview. The inputs should not be propagated to the outputs by the provider during the create because the
  2805  	// provider has unknown inputs.
  2806  	preview, sawPreview = true, false
  2807  	_, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2808  	require.Nil(t, res)
  2809  	assert.False(t, sawPreview)
  2810  
  2811  	// Run an update.
  2812  	preview, sawPreview = false, false
  2813  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, preview, p.BackendClient, nil)
  2814  	require.Nil(t, res)
  2815  	assert.False(t, sawPreview)
  2816  
  2817  	// Run another preview. The inputs should not be propagated to the outputs during the update because the provider
  2818  	// has unknown inputs.
  2819  	preview, sawPreview = true, false
  2820  	_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, preview, p.BackendClient, nil)
  2821  	require.Nil(t, res)
  2822  	assert.False(t, sawPreview)
  2823  }
  2824  
  2825  func TestSingleComponentDefaultProviderLifecycle(t *testing.T) {
  2826  	t.Parallel()
  2827  
  2828  	loaders := []*deploytest.ProviderLoader{
  2829  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2830  			construct := func(monitor *deploytest.ResourceMonitor,
  2831  				typ, name string, parent resource.URN, inputs resource.PropertyMap,
  2832  				options plugin.ConstructOptions) (plugin.ConstructResult, error) {
  2833  
  2834  				urn, _, _, err := monitor.RegisterResource(tokens.Type(typ), name, false, deploytest.ResourceOptions{
  2835  					Parent:  parent,
  2836  					Aliases: options.Aliases,
  2837  					Protect: options.Protect,
  2838  				})
  2839  				assert.NoError(t, err)
  2840  
  2841  				_, _, _, err = monitor.RegisterResource("pkgA:m:typB", "resA", true, deploytest.ResourceOptions{
  2842  					Parent: urn,
  2843  				})
  2844  				assert.NoError(t, err)
  2845  
  2846  				outs := resource.PropertyMap{"foo": resource.NewStringProperty("bar")}
  2847  				err = monitor.RegisterResourceOutputs(urn, outs)
  2848  				assert.NoError(t, err)
  2849  
  2850  				return plugin.ConstructResult{
  2851  					URN:     urn,
  2852  					Outputs: outs,
  2853  				}, nil
  2854  			}
  2855  
  2856  			return &deploytest.Provider{
  2857  				ConstructF: construct,
  2858  			}, nil
  2859  		}),
  2860  	}
  2861  
  2862  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  2863  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", false, deploytest.ResourceOptions{
  2864  			Remote: true,
  2865  		})
  2866  		assert.NoError(t, err)
  2867  		assert.Equal(t, resource.PropertyMap{
  2868  			"foo": resource.NewStringProperty("bar"),
  2869  		}, state)
  2870  		return nil
  2871  	})
  2872  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  2873  
  2874  	p := &TestPlan{
  2875  		Options: UpdateOptions{Host: host},
  2876  		Steps:   MakeBasicLifecycleSteps(t, 3),
  2877  	}
  2878  	p.Run(t, nil)
  2879  }
  2880  
  2881  type updateContext struct {
  2882  	*deploytest.ResourceMonitor
  2883  
  2884  	resmon       chan *deploytest.ResourceMonitor
  2885  	programErr   chan error
  2886  	snap         chan *deploy.Snapshot
  2887  	updateResult chan result.Result
  2888  }
  2889  
  2890  func startUpdate(t *testing.T, host plugin.Host) (*updateContext, error) {
  2891  	ctx := &updateContext{
  2892  		resmon:       make(chan *deploytest.ResourceMonitor),
  2893  		programErr:   make(chan error),
  2894  		snap:         make(chan *deploy.Snapshot),
  2895  		updateResult: make(chan result.Result),
  2896  	}
  2897  
  2898  	stop := make(chan bool)
  2899  	port, _, err := rpcutil.Serve(0, stop, []func(*grpc.Server) error{
  2900  		func(srv *grpc.Server) error {
  2901  			pulumirpc.RegisterLanguageRuntimeServer(srv, ctx)
  2902  			return nil
  2903  		},
  2904  	}, nil)
  2905  	if err != nil {
  2906  		return nil, err
  2907  	}
  2908  
  2909  	p := &TestPlan{
  2910  		Options: UpdateOptions{Host: host},
  2911  		Runtime: "client",
  2912  		RuntimeOptions: map[string]interface{}{
  2913  			"address": fmt.Sprintf("127.0.0.1:%d", port),
  2914  		},
  2915  	}
  2916  
  2917  	go func() {
  2918  		snap, res := TestOp(Update).Run(p.GetProject(), p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  2919  		ctx.snap <- snap
  2920  		close(ctx.snap)
  2921  		ctx.updateResult <- res
  2922  		close(ctx.updateResult)
  2923  		stop <- true
  2924  	}()
  2925  
  2926  	ctx.ResourceMonitor = <-ctx.resmon
  2927  	return ctx, nil
  2928  }
  2929  
  2930  func (ctx *updateContext) Finish(err error) (*deploy.Snapshot, result.Result) {
  2931  	ctx.programErr <- err
  2932  	close(ctx.programErr)
  2933  
  2934  	return <-ctx.snap, <-ctx.updateResult
  2935  }
  2936  
  2937  func (ctx *updateContext) GetRequiredPlugins(_ context.Context,
  2938  	req *pulumirpc.GetRequiredPluginsRequest) (*pulumirpc.GetRequiredPluginsResponse, error) {
  2939  	return &pulumirpc.GetRequiredPluginsResponse{}, nil
  2940  }
  2941  
  2942  func (ctx *updateContext) Run(_ context.Context, req *pulumirpc.RunRequest) (*pulumirpc.RunResponse, error) {
  2943  	// Connect to the resource monitor and create an appropriate client.
  2944  	conn, err := grpc.Dial(
  2945  		req.MonitorAddress,
  2946  		grpc.WithInsecure(),
  2947  		rpcutil.GrpcChannelOptions(),
  2948  	)
  2949  	if err != nil {
  2950  		return nil, fmt.Errorf("could not connect to resource monitor: %w", err)
  2951  	}
  2952  	defer contract.IgnoreClose(conn)
  2953  
  2954  	// Fire up a resource monitor client
  2955  	ctx.resmon <- deploytest.NewResourceMonitor(pulumirpc.NewResourceMonitorClient(conn))
  2956  	close(ctx.resmon)
  2957  
  2958  	// Wait for the program to terminate.
  2959  	if err := <-ctx.programErr; err != nil {
  2960  		return &pulumirpc.RunResponse{Error: err.Error()}, nil
  2961  	}
  2962  	return &pulumirpc.RunResponse{}, nil
  2963  }
  2964  
  2965  func (ctx *updateContext) GetPluginInfo(_ context.Context, req *pbempty.Empty) (*pulumirpc.PluginInfo, error) {
  2966  	return &pulumirpc.PluginInfo{
  2967  		Version: "1.0.0",
  2968  	}, nil
  2969  }
  2970  
  2971  func (ctx *updateContext) InstallDependencies(
  2972  	req *pulumirpc.InstallDependenciesRequest,
  2973  	server pulumirpc.LanguageRuntime_InstallDependenciesServer) error {
  2974  	return nil
  2975  }
  2976  
  2977  func (ctx *updateContext) About(_ context.Context, _ *pbempty.Empty) (*pulumirpc.AboutResponse, error) {
  2978  	return nil, status.Errorf(codes.Unimplemented, "method About not implemented")
  2979  }
  2980  
  2981  func (ctx *updateContext) GetProgramDependencies(
  2982  	_ context.Context, _ *pulumirpc.GetProgramDependenciesRequest) (*pulumirpc.GetProgramDependenciesResponse, error) {
  2983  	return nil, status.Errorf(codes.Unimplemented, "method GetProgramDependencies not implemented")
  2984  }
  2985  
  2986  func TestLanguageClient(t *testing.T) {
  2987  	t.Parallel()
  2988  
  2989  	loaders := []*deploytest.ProviderLoader{
  2990  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  2991  			return &deploytest.Provider{}, nil
  2992  		}),
  2993  	}
  2994  
  2995  	update, err := startUpdate(t, deploytest.NewPluginHost(nil, nil, nil, loaders...))
  2996  	if err != nil {
  2997  		t.Fatalf("failed to start update: %v", err)
  2998  	}
  2999  
  3000  	// Register resources, etc.
  3001  	_, _, _, err = update.RegisterResource("pkgA:m:typA", "resA", true)
  3002  	assert.NoError(t, err)
  3003  
  3004  	snap, res := update.Finish(nil)
  3005  	assert.Nil(t, res)
  3006  	assert.Len(t, snap.Resources, 2)
  3007  }
  3008  
  3009  func TestSingleComponentGetResourceDefaultProviderLifecycle(t *testing.T) {
  3010  	t.Parallel()
  3011  
  3012  	var urnB resource.URN
  3013  	var idB resource.ID
  3014  
  3015  	loaders := []*deploytest.ProviderLoader{
  3016  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3017  			construct := func(monitor *deploytest.ResourceMonitor, typ, name string, parent resource.URN,
  3018  				inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
  3019  
  3020  				urn, _, _, err := monitor.RegisterResource(tokens.Type(typ), name, false, deploytest.ResourceOptions{
  3021  					Parent:       parent,
  3022  					Protect:      options.Protect,
  3023  					Aliases:      options.Aliases,
  3024  					Dependencies: options.Dependencies,
  3025  				})
  3026  				assert.NoError(t, err)
  3027  
  3028  				urnB, idB, _, err = monitor.RegisterResource("pkgA:m:typB", "resB", true, deploytest.ResourceOptions{
  3029  					Parent: urn,
  3030  					Inputs: resource.PropertyMap{
  3031  						"bar": resource.NewStringProperty("baz"),
  3032  					},
  3033  				})
  3034  				assert.NoError(t, err)
  3035  
  3036  				return plugin.ConstructResult{
  3037  					URN: urn,
  3038  					Outputs: resource.PropertyMap{
  3039  						"foo": resource.NewStringProperty("bar"),
  3040  						"res": resource.MakeCustomResourceReference(urnB, idB, ""),
  3041  					},
  3042  				}, nil
  3043  			}
  3044  
  3045  			return &deploytest.Provider{
  3046  				CreateF: func(urn resource.URN, inputs resource.PropertyMap, timeout float64,
  3047  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3048  					return "created-id", inputs, resource.StatusOK, nil
  3049  				},
  3050  				ReadF: func(urn resource.URN, id resource.ID,
  3051  					inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
  3052  					return plugin.ReadResult{Inputs: inputs, Outputs: state}, resource.StatusOK, nil
  3053  				},
  3054  				ConstructF: construct,
  3055  			}, nil
  3056  		}),
  3057  	}
  3058  
  3059  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3060  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", false, deploytest.ResourceOptions{
  3061  			Remote: true,
  3062  		})
  3063  		assert.NoError(t, err)
  3064  		assert.Equal(t, resource.PropertyMap{
  3065  			"foo": resource.NewStringProperty("bar"),
  3066  			"res": resource.MakeCustomResourceReference(urnB, idB, ""),
  3067  		}, state)
  3068  
  3069  		result, _, err := monitor.Invoke("pulumi:pulumi:getResource", resource.PropertyMap{
  3070  			"urn": resource.NewStringProperty(string(urnB)),
  3071  		}, "", "")
  3072  		assert.NoError(t, err)
  3073  		assert.Equal(t, resource.PropertyMap{
  3074  			"urn": resource.NewStringProperty(string(urnB)),
  3075  			"id":  resource.NewStringProperty(string(idB)),
  3076  			"state": resource.NewObjectProperty(resource.PropertyMap{
  3077  				"bar": resource.NewStringProperty("baz"),
  3078  			}),
  3079  		}, result)
  3080  		return nil
  3081  	})
  3082  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3083  
  3084  	p := &TestPlan{
  3085  		Options: UpdateOptions{Host: host},
  3086  		Steps:   MakeBasicLifecycleSteps(t, 4),
  3087  	}
  3088  	p.Run(t, nil)
  3089  }
  3090  
  3091  func TestConfigSecrets(t *testing.T) {
  3092  	t.Parallel()
  3093  
  3094  	loaders := []*deploytest.ProviderLoader{
  3095  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3096  			return &deploytest.Provider{}, nil
  3097  		}),
  3098  	}
  3099  
  3100  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3101  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true)
  3102  		assert.NoError(t, err)
  3103  		return nil
  3104  	})
  3105  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3106  
  3107  	crypter := config.NewSymmetricCrypter(make([]byte, 32))
  3108  	secret, err := crypter.EncryptValue(context.Background(), "hunter2")
  3109  	assert.NoError(t, err)
  3110  
  3111  	p := &TestPlan{
  3112  		Options: UpdateOptions{Host: host},
  3113  		Steps:   MakeBasicLifecycleSteps(t, 2),
  3114  		Config: config.Map{
  3115  			config.MustMakeKey("pkgA", "secret"): config.NewSecureValue(secret),
  3116  		},
  3117  		Decrypter: crypter,
  3118  	}
  3119  
  3120  	project := p.GetProject()
  3121  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  3122  	assert.Nil(t, res)
  3123  
  3124  	if !assert.Len(t, snap.Resources, 2) {
  3125  		return
  3126  	}
  3127  
  3128  	provider := snap.Resources[0]
  3129  	assert.True(t, provider.Inputs["secret"].IsSecret())
  3130  	assert.True(t, provider.Outputs["secret"].IsSecret())
  3131  }
  3132  
  3133  func TestComponentOutputs(t *testing.T) {
  3134  	t.Parallel()
  3135  
  3136  	// A component's outputs should never be returned by `RegisterResource`, even if (especially if) there are
  3137  	// outputs from a prior deployment and the component's inputs have not changed.
  3138  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3139  		urn, _, state, err := monitor.RegisterResource("component", "resA", false)
  3140  		assert.NoError(t, err)
  3141  		assert.Equal(t, resource.PropertyMap{}, state)
  3142  
  3143  		err = monitor.RegisterResourceOutputs(urn, resource.PropertyMap{
  3144  			"foo": resource.NewStringProperty("bar"),
  3145  		})
  3146  		assert.NoError(t, err)
  3147  		return nil
  3148  	})
  3149  	host := deploytest.NewPluginHost(nil, nil, program)
  3150  
  3151  	p := &TestPlan{
  3152  		Options: UpdateOptions{Host: host},
  3153  		Steps:   MakeBasicLifecycleSteps(t, 1),
  3154  	}
  3155  	p.Run(t, nil)
  3156  }
  3157  
  3158  // Test calling a method.
  3159  func TestSingleComponentMethodDefaultProviderLifecycle(t *testing.T) {
  3160  	t.Parallel()
  3161  
  3162  	var urn resource.URN
  3163  
  3164  	loaders := []*deploytest.ProviderLoader{
  3165  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3166  			construct := func(monitor *deploytest.ResourceMonitor,
  3167  				typ, name string, parent resource.URN, inputs resource.PropertyMap,
  3168  				options plugin.ConstructOptions) (plugin.ConstructResult, error) {
  3169  
  3170  				var err error
  3171  				urn, _, _, err = monitor.RegisterResource(tokens.Type(typ), name, false, deploytest.ResourceOptions{
  3172  					Parent:  parent,
  3173  					Aliases: options.Aliases,
  3174  					Protect: options.Protect,
  3175  				})
  3176  				assert.NoError(t, err)
  3177  
  3178  				_, _, _, err = monitor.RegisterResource("pkgA:m:typB", "resA", true, deploytest.ResourceOptions{
  3179  					Parent: urn,
  3180  				})
  3181  				assert.NoError(t, err)
  3182  
  3183  				outs := resource.PropertyMap{"foo": resource.NewStringProperty("bar")}
  3184  				err = monitor.RegisterResourceOutputs(urn, outs)
  3185  				assert.NoError(t, err)
  3186  
  3187  				return plugin.ConstructResult{
  3188  					URN:     urn,
  3189  					Outputs: outs,
  3190  				}, nil
  3191  			}
  3192  
  3193  			call := func(monitor *deploytest.ResourceMonitor, tok tokens.ModuleMember, args resource.PropertyMap,
  3194  				info plugin.CallInfo, options plugin.CallOptions) (plugin.CallResult, error) {
  3195  
  3196  				assert.Equal(t, resource.PropertyMap{
  3197  					"name": resource.NewStringProperty("Alice"),
  3198  				}, args)
  3199  				name := args["name"].StringValue()
  3200  
  3201  				result, _, err := monitor.Invoke("pulumi:pulumi:getResource", resource.PropertyMap{
  3202  					"urn": resource.NewStringProperty(string(urn)),
  3203  				}, "", "")
  3204  				assert.NoError(t, err)
  3205  				state := result["state"]
  3206  				foo := state.ObjectValue()["foo"].StringValue()
  3207  
  3208  				message := fmt.Sprintf("%s, %s!", name, foo)
  3209  				return plugin.CallResult{
  3210  					Return: resource.PropertyMap{
  3211  						"message": resource.NewStringProperty(message),
  3212  					},
  3213  				}, nil
  3214  			}
  3215  
  3216  			return &deploytest.Provider{
  3217  				ConstructF: construct,
  3218  				CallF:      call,
  3219  			}, nil
  3220  		}),
  3221  	}
  3222  
  3223  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3224  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", false, deploytest.ResourceOptions{
  3225  			Remote: true,
  3226  		})
  3227  		assert.NoError(t, err)
  3228  		assert.Equal(t, resource.PropertyMap{
  3229  			"foo": resource.NewStringProperty("bar"),
  3230  		}, state)
  3231  
  3232  		outs, _, _, err := monitor.Call("pkgA:m:typA/methodA", resource.PropertyMap{
  3233  			"name": resource.NewStringProperty("Alice"),
  3234  		}, "", "")
  3235  		assert.NoError(t, err)
  3236  		assert.Equal(t, resource.PropertyMap{
  3237  			"message": resource.NewStringProperty("Alice, bar!"),
  3238  		}, outs)
  3239  
  3240  		return nil
  3241  	})
  3242  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3243  
  3244  	p := &TestPlan{
  3245  		Options: UpdateOptions{Host: host},
  3246  		Steps:   MakeBasicLifecycleSteps(t, 4),
  3247  	}
  3248  	p.Run(t, nil)
  3249  }
  3250  
  3251  // Test creating a resource from a method.
  3252  func TestSingleComponentMethodResourceDefaultProviderLifecycle(t *testing.T) {
  3253  	t.Parallel()
  3254  
  3255  	var urn resource.URN
  3256  
  3257  	loaders := []*deploytest.ProviderLoader{
  3258  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3259  			construct := func(monitor *deploytest.ResourceMonitor,
  3260  				typ, name string, parent resource.URN, inputs resource.PropertyMap,
  3261  				options plugin.ConstructOptions) (plugin.ConstructResult, error) {
  3262  
  3263  				var err error
  3264  				urn, _, _, err = monitor.RegisterResource(tokens.Type(typ), name, false, deploytest.ResourceOptions{
  3265  					Parent:  parent,
  3266  					Aliases: options.Aliases,
  3267  					Protect: options.Protect,
  3268  				})
  3269  				assert.NoError(t, err)
  3270  
  3271  				_, _, _, err = monitor.RegisterResource("pkgA:m:typB", "resA", true, deploytest.ResourceOptions{
  3272  					Parent: urn,
  3273  				})
  3274  				assert.NoError(t, err)
  3275  
  3276  				outs := resource.PropertyMap{"foo": resource.NewStringProperty("bar")}
  3277  				err = monitor.RegisterResourceOutputs(urn, outs)
  3278  				assert.NoError(t, err)
  3279  
  3280  				return plugin.ConstructResult{
  3281  					URN:     urn,
  3282  					Outputs: outs,
  3283  				}, nil
  3284  			}
  3285  
  3286  			call := func(monitor *deploytest.ResourceMonitor, tok tokens.ModuleMember, args resource.PropertyMap,
  3287  				info plugin.CallInfo, options plugin.CallOptions) (plugin.CallResult, error) {
  3288  
  3289  				_, _, _, err := monitor.RegisterResource("pkgA:m:typC", "resA", true, deploytest.ResourceOptions{
  3290  					Parent: urn,
  3291  				})
  3292  				assert.NoError(t, err)
  3293  
  3294  				return plugin.CallResult{}, nil
  3295  			}
  3296  
  3297  			return &deploytest.Provider{
  3298  				ConstructF: construct,
  3299  				CallF:      call,
  3300  			}, nil
  3301  		}),
  3302  	}
  3303  
  3304  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3305  		_, _, state, err := monitor.RegisterResource("pkgA:m:typA", "resA", false, deploytest.ResourceOptions{
  3306  			Remote: true,
  3307  		})
  3308  		assert.NoError(t, err)
  3309  		assert.Equal(t, resource.PropertyMap{
  3310  			"foo": resource.NewStringProperty("bar"),
  3311  		}, state)
  3312  
  3313  		_, _, _, err = monitor.Call("pkgA:m:typA/methodA", resource.PropertyMap{}, "", "")
  3314  		assert.NoError(t, err)
  3315  		return nil
  3316  	})
  3317  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3318  
  3319  	p := &TestPlan{
  3320  		Options: UpdateOptions{Host: host},
  3321  		Steps:   MakeBasicLifecycleSteps(t, 4),
  3322  	}
  3323  	p.Run(t, nil)
  3324  }
  3325  
  3326  // This tests a scenario involving two remote components with interdependencies that are only represented in the
  3327  // user program.
  3328  func TestComponentDeleteDependencies(t *testing.T) {
  3329  	t.Parallel()
  3330  
  3331  	var (
  3332  		firstURN  resource.URN
  3333  		nestedURN resource.URN
  3334  		sgURN     resource.URN
  3335  		secondURN resource.URN
  3336  		ruleURN   resource.URN
  3337  
  3338  		err error
  3339  	)
  3340  
  3341  	loaders := []*deploytest.ProviderLoader{
  3342  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3343  			return &deploytest.Provider{}, nil
  3344  		}),
  3345  		deploytest.NewProviderLoader("pkgB", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3346  			return &deploytest.Provider{
  3347  				ConstructF: func(monitor *deploytest.ResourceMonitor, typ, name string, parent resource.URN,
  3348  					inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
  3349  
  3350  					switch typ {
  3351  					case "pkgB:m:first":
  3352  						firstURN, _, _, err = monitor.RegisterResource("pkgB:m:first", name, false)
  3353  						require.NoError(t, err)
  3354  
  3355  						nestedURN, _, _, err = monitor.RegisterResource("nested", "nested", false,
  3356  							deploytest.ResourceOptions{
  3357  								Parent: firstURN,
  3358  							})
  3359  						require.NoError(t, err)
  3360  
  3361  						sgURN, _, _, err = monitor.RegisterResource("pkgA:m:sg", "sg", true, deploytest.ResourceOptions{
  3362  							Parent: nestedURN,
  3363  						})
  3364  						require.NoError(t, err)
  3365  
  3366  						err = monitor.RegisterResourceOutputs(nestedURN, resource.PropertyMap{})
  3367  						require.NoError(t, err)
  3368  
  3369  						err = monitor.RegisterResourceOutputs(firstURN, resource.PropertyMap{})
  3370  						require.NoError(t, err)
  3371  
  3372  						return plugin.ConstructResult{URN: firstURN}, nil
  3373  					case "pkgB:m:second":
  3374  						secondURN, _, _, err = monitor.RegisterResource("pkgB:m:second", name, false,
  3375  							deploytest.ResourceOptions{
  3376  								Dependencies: options.Dependencies,
  3377  							})
  3378  						require.NoError(t, err)
  3379  
  3380  						ruleURN, _, _, err = monitor.RegisterResource("pkgA:m:rule", "rule", true,
  3381  							deploytest.ResourceOptions{
  3382  								Parent:       secondURN,
  3383  								Dependencies: options.PropertyDependencies["sgID"],
  3384  							})
  3385  						require.NoError(t, err)
  3386  
  3387  						err = monitor.RegisterResourceOutputs(secondURN, resource.PropertyMap{})
  3388  						require.NoError(t, err)
  3389  
  3390  						return plugin.ConstructResult{URN: secondURN}, nil
  3391  					default:
  3392  						return plugin.ConstructResult{}, fmt.Errorf("unexpected type %v", typ)
  3393  					}
  3394  				},
  3395  			}, nil
  3396  		}),
  3397  	}
  3398  
  3399  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3400  		_, _, _, err = monitor.RegisterResource("pkgB:m:first", "first", false, deploytest.ResourceOptions{
  3401  			Remote: true,
  3402  		})
  3403  		require.NoError(t, err)
  3404  
  3405  		_, _, _, err = monitor.RegisterResource("pkgB:m:second", "second", false, deploytest.ResourceOptions{
  3406  			Remote: true,
  3407  			PropertyDeps: map[resource.PropertyKey][]resource.URN{
  3408  				"sgID": {sgURN},
  3409  			},
  3410  			Dependencies: []resource.URN{firstURN},
  3411  		})
  3412  		require.NoError(t, err)
  3413  
  3414  		return nil
  3415  	})
  3416  
  3417  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3418  	p := &TestPlan{Options: UpdateOptions{Host: host}}
  3419  
  3420  	p.Steps = []TestStep{
  3421  		{
  3422  			Op:          Update,
  3423  			SkipPreview: true,
  3424  		},
  3425  		{
  3426  			Op:          Destroy,
  3427  			SkipPreview: true,
  3428  			Validate: func(project workspace.Project, target deploy.Target, entries JournalEntries,
  3429  				evts []Event, res result.Result) result.Result {
  3430  				assert.Nil(t, res)
  3431  
  3432  				firstIndex, nestedIndex, sgIndex, secondIndex, ruleIndex := -1, -1, -1, -1, -1
  3433  
  3434  				for i, entry := range entries {
  3435  					switch urn := entry.Step.URN(); urn {
  3436  					case firstURN:
  3437  						firstIndex = i
  3438  					case nestedURN:
  3439  						nestedIndex = i
  3440  					case sgURN:
  3441  						sgIndex = i
  3442  					case secondURN:
  3443  						secondIndex = i
  3444  					case ruleURN:
  3445  						ruleIndex = i
  3446  					}
  3447  				}
  3448  
  3449  				assert.Less(t, ruleIndex, sgIndex)
  3450  				assert.Less(t, ruleIndex, secondIndex)
  3451  				assert.Less(t, secondIndex, firstIndex)
  3452  				assert.Less(t, secondIndex, sgIndex)
  3453  				assert.Less(t, sgIndex, nestedIndex)
  3454  				assert.Less(t, nestedIndex, firstIndex)
  3455  
  3456  				return res
  3457  			},
  3458  		},
  3459  	}
  3460  	p.Run(t, nil)
  3461  }
  3462  
  3463  func TestProtect(t *testing.T) {
  3464  	t.Parallel()
  3465  
  3466  	idCounter := 0
  3467  	deleteCounter := 0
  3468  
  3469  	loaders := []*deploytest.ProviderLoader{
  3470  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3471  			return &deploytest.Provider{
  3472  				DiffF: func(
  3473  					urn resource.URN,
  3474  					id resource.ID,
  3475  					olds, news resource.PropertyMap,
  3476  					ignoreChanges []string) (plugin.DiffResult, error) {
  3477  					if !olds["foo"].DeepEquals(news["foo"]) {
  3478  						// If foo changes do a replace, we use this to check we don't delete on replace
  3479  						return plugin.DiffResult{
  3480  							Changes:     plugin.DiffSome,
  3481  							ReplaceKeys: []resource.PropertyKey{"foo"},
  3482  						}, nil
  3483  					}
  3484  					return plugin.DiffResult{}, nil
  3485  				},
  3486  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  3487  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3488  					resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter))
  3489  					idCounter = idCounter + 1
  3490  					return resourceID, news, resource.StatusOK, nil
  3491  				},
  3492  				DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
  3493  					timeout float64) (resource.Status, error) {
  3494  					deleteCounter = deleteCounter + 1
  3495  					return resource.StatusOK, nil
  3496  				},
  3497  			}, nil
  3498  		}, deploytest.WithoutGrpc),
  3499  	}
  3500  
  3501  	ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  3502  		"foo": "bar",
  3503  	})
  3504  
  3505  	shouldProtect := true
  3506  	createResource := true
  3507  
  3508  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3509  
  3510  		if createResource {
  3511  			_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  3512  				Inputs:  ins,
  3513  				Protect: shouldProtect,
  3514  			})
  3515  			assert.NoError(t, err)
  3516  		}
  3517  
  3518  		return nil
  3519  	})
  3520  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3521  
  3522  	p := &TestPlan{
  3523  		Options: UpdateOptions{Host: host},
  3524  	}
  3525  
  3526  	project := p.GetProject()
  3527  
  3528  	// Run an update to create the resource
  3529  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  3530  	assert.Nil(t, res)
  3531  	assert.NotNil(t, snap)
  3532  	assert.Len(t, snap.Resources, 2)
  3533  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3534  	assert.Equal(t, 0, deleteCounter)
  3535  
  3536  	expectedUrn := snap.Resources[1].URN
  3537  	expectedMessage := ""
  3538  
  3539  	// Both updates below should give a diagnostic event
  3540  	validate := func(project workspace.Project,
  3541  		target deploy.Target, entries JournalEntries,
  3542  		events []Event, res result.Result) result.Result {
  3543  		for _, event := range events {
  3544  			if event.Type == DiagEvent {
  3545  				payload := event.Payload().(DiagEventPayload)
  3546  				assert.Equal(t, expectedUrn, payload.URN)
  3547  				assert.Equal(t, expectedMessage, payload.Message)
  3548  				break
  3549  			}
  3550  		}
  3551  		return res
  3552  	}
  3553  
  3554  	// Run a new update which will cause a replace, we should get an error
  3555  	expectedMessage = "<{%reset%}>unable to replace resource \"urn:pulumi:test::test::pkgA:m:typA::resA\"\n" +
  3556  		"as it is currently marked for protection. To unprotect the resource, remove the `protect` flag from " +
  3557  		"the resource in your Pulumi program and run `pulumi up`<{%reset%}>\n"
  3558  	ins = resource.NewPropertyMapFromMap(map[string]interface{}{
  3559  		"foo": "baz",
  3560  	})
  3561  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, validate)
  3562  	assert.NotNil(t, res)
  3563  	assert.NotNil(t, snap)
  3564  	assert.Len(t, snap.Resources, 2)
  3565  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3566  	assert.Equal(t, 0, deleteCounter)
  3567  
  3568  	// Run a new update which will cause a delete, we still shouldn't see a provider delete
  3569  	expectedMessage = "<{%reset%}>unable to delete resource \"urn:pulumi:test::test::pkgA:m:typA::resA\"\n" +
  3570  		"as it is currently marked for protection. To unprotect the resource, either remove the `protect` flag " +
  3571  		"from the resource in your Pulumi program and run `pulumi up` or use the command:\n" +
  3572  		"`pulumi state unprotect 'urn:pulumi:test::test::pkgA:m:typA::resA'`<{%reset%}>\n"
  3573  	createResource = false
  3574  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, validate)
  3575  	assert.NotNil(t, res)
  3576  	assert.NotNil(t, snap)
  3577  	assert.Len(t, snap.Resources, 2)
  3578  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3579  	assert.Equal(t, true, snap.Resources[1].Protect)
  3580  	assert.Equal(t, 0, deleteCounter)
  3581  
  3582  	// Run a new update to remove the protect and replace in the same update, this should delete the old one
  3583  	// and create the new one
  3584  	createResource = true
  3585  	shouldProtect = false
  3586  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3587  	assert.Nil(t, res)
  3588  	assert.NotNil(t, snap)
  3589  	assert.Len(t, snap.Resources, 2)
  3590  	assert.Equal(t, "created-id-1", snap.Resources[1].ID.String())
  3591  	assert.Equal(t, false, snap.Resources[1].Protect)
  3592  	assert.Equal(t, 1, deleteCounter)
  3593  
  3594  	// Run a new update to add the protect flag, nothing else should change
  3595  	shouldProtect = true
  3596  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3597  	assert.Nil(t, res)
  3598  	assert.NotNil(t, snap)
  3599  	assert.Len(t, snap.Resources, 2)
  3600  	assert.Equal(t, "created-id-1", snap.Resources[1].ID.String())
  3601  	assert.Equal(t, true, snap.Resources[1].Protect)
  3602  	assert.Equal(t, 1, deleteCounter)
  3603  
  3604  	// Edit the snapshot to remove the protect flag and try and replace
  3605  	snap.Resources[1].Protect = false
  3606  	ins = resource.NewPropertyMapFromMap(map[string]interface{}{
  3607  		"foo": "daz",
  3608  	})
  3609  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, validate)
  3610  	assert.Nil(t, res)
  3611  	assert.NotNil(t, snap)
  3612  	assert.Len(t, snap.Resources, 2)
  3613  	assert.Equal(t, "created-id-2", snap.Resources[1].ID.String())
  3614  	assert.Equal(t, 2, deleteCounter)
  3615  }
  3616  
  3617  func TestRetainOnDelete(t *testing.T) {
  3618  	t.Parallel()
  3619  
  3620  	idCounter := 0
  3621  
  3622  	loaders := []*deploytest.ProviderLoader{
  3623  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3624  			return &deploytest.Provider{
  3625  				DiffF: func(
  3626  					urn resource.URN,
  3627  					id resource.ID,
  3628  					olds, news resource.PropertyMap,
  3629  					ignoreChanges []string) (plugin.DiffResult, error) {
  3630  					if !olds["foo"].DeepEquals(news["foo"]) {
  3631  						// If foo changes do a replace, we use this to check we don't delete on replace
  3632  						return plugin.DiffResult{
  3633  							Changes:     plugin.DiffSome,
  3634  							ReplaceKeys: []resource.PropertyKey{"foo"},
  3635  						}, nil
  3636  					}
  3637  					return plugin.DiffResult{}, nil
  3638  				},
  3639  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  3640  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3641  					resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter))
  3642  					idCounter = idCounter + 1
  3643  					return resourceID, news, resource.StatusOK, nil
  3644  				},
  3645  				DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
  3646  					timeout float64) (resource.Status, error) {
  3647  					assert.Fail(t, "Delete was called")
  3648  					return resource.StatusOK, nil
  3649  				},
  3650  			}, nil
  3651  		}, deploytest.WithoutGrpc),
  3652  	}
  3653  
  3654  	ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  3655  		"foo": "bar",
  3656  	})
  3657  
  3658  	createResource := true
  3659  
  3660  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3661  
  3662  		if createResource {
  3663  			_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  3664  				Inputs:         ins,
  3665  				RetainOnDelete: true,
  3666  			})
  3667  			assert.NoError(t, err)
  3668  		}
  3669  
  3670  		return nil
  3671  	})
  3672  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3673  
  3674  	p := &TestPlan{
  3675  		Options: UpdateOptions{Host: host},
  3676  	}
  3677  
  3678  	project := p.GetProject()
  3679  
  3680  	// Run an update to create the resource
  3681  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  3682  	assert.Nil(t, res)
  3683  	assert.NotNil(t, snap)
  3684  	assert.Len(t, snap.Resources, 2)
  3685  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3686  
  3687  	// Run a new update which will cause a replace, we shouldn't see a provider delete but should get a new id
  3688  	ins = resource.NewPropertyMapFromMap(map[string]interface{}{
  3689  		"foo": "baz",
  3690  	})
  3691  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3692  	assert.Nil(t, res)
  3693  	assert.NotNil(t, snap)
  3694  	assert.Len(t, snap.Resources, 2)
  3695  	assert.Equal(t, "created-id-1", snap.Resources[1].ID.String())
  3696  
  3697  	// Run a new update which will cause a delete, we still shouldn't see a provider delete
  3698  	createResource = false
  3699  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3700  	assert.Nil(t, res)
  3701  	assert.NotNil(t, snap)
  3702  	assert.Len(t, snap.Resources, 0)
  3703  }
  3704  
  3705  func TestDeletedWith(t *testing.T) {
  3706  	t.Parallel()
  3707  
  3708  	idCounter := 0
  3709  
  3710  	topURN := resource.URN("")
  3711  
  3712  	loaders := []*deploytest.ProviderLoader{
  3713  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3714  			return &deploytest.Provider{
  3715  				DiffF: func(
  3716  					urn resource.URN,
  3717  					id resource.ID,
  3718  					olds, news resource.PropertyMap,
  3719  					ignoreChanges []string) (plugin.DiffResult, error) {
  3720  					if !olds["foo"].DeepEquals(news["foo"]) {
  3721  						// If foo changes do a replace, we use this to check we don't delete on replace
  3722  						return plugin.DiffResult{
  3723  							Changes:     plugin.DiffSome,
  3724  							ReplaceKeys: []resource.PropertyKey{"foo"},
  3725  						}, nil
  3726  					}
  3727  					return plugin.DiffResult{}, nil
  3728  				},
  3729  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  3730  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3731  					resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter))
  3732  					idCounter = idCounter + 1
  3733  					return resourceID, news, resource.StatusOK, nil
  3734  				},
  3735  				DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
  3736  					timeout float64) (resource.Status, error) {
  3737  					if urn != topURN {
  3738  						// Only topURN (aURN) should be actually deleted
  3739  						assert.Fail(t, "Delete was called")
  3740  					}
  3741  					return resource.StatusOK, nil
  3742  				},
  3743  			}, nil
  3744  		}, deploytest.WithoutGrpc),
  3745  	}
  3746  
  3747  	ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  3748  		"foo": "bar",
  3749  	})
  3750  
  3751  	createResource := true
  3752  
  3753  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3754  
  3755  		if createResource {
  3756  			aURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  3757  				Inputs: ins,
  3758  			})
  3759  			assert.NoError(t, err)
  3760  			topURN = aURN
  3761  
  3762  			bURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resB", true, deploytest.ResourceOptions{
  3763  				Inputs:      ins,
  3764  				DeletedWith: aURN,
  3765  			})
  3766  			assert.NoError(t, err)
  3767  
  3768  			_, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resC", true, deploytest.ResourceOptions{
  3769  				Inputs:      ins,
  3770  				DeletedWith: bURN,
  3771  			})
  3772  			assert.NoError(t, err)
  3773  		}
  3774  
  3775  		return nil
  3776  	})
  3777  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3778  
  3779  	p := &TestPlan{
  3780  		Options: UpdateOptions{Host: host},
  3781  	}
  3782  
  3783  	project := p.GetProject()
  3784  
  3785  	// Run an update to create the resource
  3786  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  3787  	assert.Nil(t, res)
  3788  	assert.NotNil(t, snap)
  3789  	assert.Len(t, snap.Resources, 4)
  3790  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3791  	assert.Equal(t, "created-id-1", snap.Resources[2].ID.String())
  3792  	assert.Equal(t, "created-id-2", snap.Resources[3].ID.String())
  3793  
  3794  	// Run a new update which will cause a replace, we should only see a provider delete for aURN but should
  3795  	// get a new id for everything
  3796  	ins = resource.NewPropertyMapFromMap(map[string]interface{}{
  3797  		"foo": "baz",
  3798  	})
  3799  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3800  	assert.Nil(t, res)
  3801  	assert.NotNil(t, snap)
  3802  	assert.Len(t, snap.Resources, 4)
  3803  	assert.Equal(t, "created-id-3", snap.Resources[1].ID.String())
  3804  	assert.Equal(t, "created-id-4", snap.Resources[2].ID.String())
  3805  	assert.Equal(t, "created-id-5", snap.Resources[3].ID.String())
  3806  
  3807  	// Run a new update which will cause a delete, we still shouldn't see a provider delete for anything but aURN
  3808  	createResource = false
  3809  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3810  	assert.Nil(t, res)
  3811  	assert.NotNil(t, snap)
  3812  	assert.Len(t, snap.Resources, 0)
  3813  }
  3814  
  3815  func TestDeletedWithCircularDependency(t *testing.T) {
  3816  	// This test should be removed if DeletedWith circular dependency is taken care of.
  3817  	// At the mean time, if there is a circular dependency - none shall be deleted.
  3818  	t.Parallel()
  3819  
  3820  	idCounter := 0
  3821  
  3822  	loaders := []*deploytest.ProviderLoader{
  3823  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3824  			return &deploytest.Provider{
  3825  				DiffF: func(
  3826  					urn resource.URN,
  3827  					id resource.ID,
  3828  					olds, news resource.PropertyMap,
  3829  					ignoreChanges []string) (plugin.DiffResult, error) {
  3830  					return plugin.DiffResult{}, nil
  3831  				},
  3832  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  3833  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3834  					resourceID := resource.ID(fmt.Sprintf("created-id-%d", idCounter))
  3835  					idCounter = idCounter + 1
  3836  					return resourceID, news, resource.StatusOK, nil
  3837  				},
  3838  				DeleteF: func(urn resource.URN, id resource.ID, olds resource.PropertyMap,
  3839  					timeout float64) (resource.Status, error) {
  3840  
  3841  					assert.Fail(t, "Delete was called")
  3842  
  3843  					return resource.StatusOK, nil
  3844  				},
  3845  			}, nil
  3846  		}, deploytest.WithoutGrpc),
  3847  	}
  3848  
  3849  	ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  3850  		"foo": "bar",
  3851  	})
  3852  
  3853  	createResource := true
  3854  	cURN := resource.URN("")
  3855  
  3856  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3857  
  3858  		if createResource {
  3859  			aURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  3860  				Inputs:      ins,
  3861  				DeletedWith: cURN,
  3862  			})
  3863  			assert.NoError(t, err)
  3864  
  3865  			bURN, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resB", true, deploytest.ResourceOptions{
  3866  				Inputs:      ins,
  3867  				DeletedWith: aURN,
  3868  			})
  3869  			assert.NoError(t, err)
  3870  
  3871  			cURN, _, _, err = monitor.RegisterResource("pkgA:m:typA", "resC", true, deploytest.ResourceOptions{
  3872  				Inputs:      ins,
  3873  				DeletedWith: bURN,
  3874  			})
  3875  			assert.NoError(t, err)
  3876  		}
  3877  
  3878  		return nil
  3879  	})
  3880  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3881  
  3882  	p := &TestPlan{
  3883  		Options: UpdateOptions{Host: host},
  3884  	}
  3885  
  3886  	project := p.GetProject()
  3887  
  3888  	// Run an update to create the resource
  3889  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  3890  	assert.Nil(t, res)
  3891  	assert.NotNil(t, snap)
  3892  	assert.Len(t, snap.Resources, 4)
  3893  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3894  	assert.Equal(t, "created-id-1", snap.Resources[2].ID.String())
  3895  	assert.Equal(t, "created-id-2", snap.Resources[3].ID.String())
  3896  
  3897  	// Run again to update DeleteWith for resA
  3898  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3899  	assert.Nil(t, res)
  3900  	assert.NotNil(t, snap)
  3901  	assert.Len(t, snap.Resources, 4)
  3902  	assert.Equal(t, "created-id-0", snap.Resources[1].ID.String())
  3903  	assert.Equal(t, "created-id-1", snap.Resources[2].ID.String())
  3904  	assert.Equal(t, "created-id-2", snap.Resources[3].ID.String())
  3905  
  3906  	// Run a new update which will cause a delete, we still shouldn't see a provider delete
  3907  	createResource = false
  3908  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  3909  	assert.Nil(t, res)
  3910  	assert.NotNil(t, snap)
  3911  	assert.Len(t, snap.Resources, 0)
  3912  }
  3913  
  3914  func TestInvalidGetIDReportsUserError(t *testing.T) {
  3915  	t.Parallel()
  3916  
  3917  	loaders := []*deploytest.ProviderLoader{
  3918  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3919  			return &deploytest.Provider{}, nil
  3920  		}, deploytest.WithoutGrpc),
  3921  	}
  3922  
  3923  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3924  		_, _, err := monitor.ReadResource("pkgA:m:typA", "resA", "", "", resource.PropertyMap{}, "", "")
  3925  		assert.Error(t, err)
  3926  		return nil
  3927  	})
  3928  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3929  
  3930  	p := &TestPlan{
  3931  		Options: UpdateOptions{Host: host},
  3932  	}
  3933  
  3934  	project := p.GetProject()
  3935  
  3936  	validate := ExpectDiagMessage(t, regexp.QuoteMeta(
  3937  		"<{%reset%}>Expected an ID for urn:pulumi:test::test::pkgA:m:typA::resA<{%reset%}>"))
  3938  
  3939  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, validate)
  3940  	assert.Nil(t, res)
  3941  	assert.NotNil(t, snap)
  3942  	assert.Len(t, snap.Resources, 1)
  3943  }
  3944  
  3945  func TestEventSecrets(t *testing.T) {
  3946  	t.Parallel()
  3947  
  3948  	loaders := []*deploytest.ProviderLoader{
  3949  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  3950  			return &deploytest.Provider{
  3951  				DiffF: func(urn resource.URN, id resource.ID,
  3952  					olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  3953  
  3954  					diff := olds.Diff(news)
  3955  					if diff == nil {
  3956  						return plugin.DiffResult{Changes: plugin.DiffNone}, nil
  3957  					}
  3958  					detailedDiff := plugin.NewDetailedDiffFromObjectDiff(diff)
  3959  					changedKeys := diff.ChangedKeys()
  3960  
  3961  					return plugin.DiffResult{
  3962  						Changes:      plugin.DiffSome,
  3963  						ChangedKeys:  changedKeys,
  3964  						DetailedDiff: detailedDiff,
  3965  					}, nil
  3966  				},
  3967  
  3968  				UpdateF: func(urn resource.URN, id resource.ID, olds, news resource.PropertyMap, timeout float64,
  3969  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  3970  					return news, resource.StatusOK, nil
  3971  				},
  3972  				CreateF: func(urn resource.URN, inputs resource.PropertyMap, timeout float64,
  3973  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  3974  					return resource.ID("id123"), inputs, resource.StatusOK, nil
  3975  				},
  3976  			}, nil
  3977  		}),
  3978  	}
  3979  
  3980  	var inputs resource.PropertyMap
  3981  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  3982  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  3983  			Inputs: inputs,
  3984  		})
  3985  		assert.NoError(t, err)
  3986  		return nil
  3987  	})
  3988  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  3989  
  3990  	p := &TestPlan{
  3991  		Options: UpdateOptions{Host: host},
  3992  		Steps: []TestStep{{
  3993  			Op:          Update,
  3994  			SkipPreview: true,
  3995  		}},
  3996  	}
  3997  
  3998  	inputs = resource.PropertyMap{
  3999  		"webhooks": resource.MakeSecret(resource.NewArrayProperty([]resource.PropertyValue{
  4000  			resource.NewObjectProperty(resource.PropertyMap{
  4001  				"clientConfig": resource.NewObjectProperty(resource.PropertyMap{
  4002  					"service": resource.NewStringProperty("foo"),
  4003  				}),
  4004  			}),
  4005  		})),
  4006  	}
  4007  	snap := p.Run(t, nil)
  4008  
  4009  	inputs = resource.PropertyMap{
  4010  		"webhooks": resource.MakeSecret(resource.NewArrayProperty([]resource.PropertyValue{
  4011  			resource.NewObjectProperty(resource.PropertyMap{
  4012  				"clientConfig": resource.NewObjectProperty(resource.PropertyMap{
  4013  					"service": resource.NewStringProperty("bar"),
  4014  				}),
  4015  			}),
  4016  		})),
  4017  	}
  4018  	p.Steps[0].Validate = func(project workspace.Project, target deploy.Target, entries JournalEntries,
  4019  		evts []Event, res result.Result) result.Result {
  4020  
  4021  		for _, e := range evts {
  4022  			var step StepEventMetadata
  4023  			switch e.Type {
  4024  			case ResourcePreEvent:
  4025  				step = e.Payload().(ResourcePreEventPayload).Metadata
  4026  			case ResourceOutputsEvent:
  4027  				step = e.Payload().(ResourceOutputsEventPayload).Metadata
  4028  			default:
  4029  				continue
  4030  			}
  4031  			if step.URN.Name() != "resA" {
  4032  				continue
  4033  			}
  4034  
  4035  			assert.True(t, step.Old.Inputs["webhooks"].IsSecret())
  4036  			assert.True(t, step.Old.Outputs["webhooks"].IsSecret())
  4037  			assert.True(t, step.New.Inputs["webhooks"].IsSecret())
  4038  		}
  4039  		return res
  4040  	}
  4041  	p.Run(t, snap)
  4042  }
  4043  
  4044  func TestAdditionalSecretOutputs(t *testing.T) {
  4045  	t.Parallel()
  4046  
  4047  	t.Skip("AdditionalSecretOutputs warning is currently disabled")
  4048  
  4049  	loaders := []*deploytest.ProviderLoader{
  4050  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  4051  			return &deploytest.Provider{
  4052  				CreateF: func(urn resource.URN, inputs resource.PropertyMap, timeout float64,
  4053  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  4054  					return resource.ID("id123"), inputs, resource.StatusOK, nil
  4055  				},
  4056  			}, nil
  4057  		}),
  4058  	}
  4059  
  4060  	var inputs resource.PropertyMap
  4061  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  4062  		_, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  4063  			Inputs:                  inputs,
  4064  			AdditionalSecretOutputs: []resource.PropertyKey{"a", "b"},
  4065  		})
  4066  		assert.NoError(t, err)
  4067  		return nil
  4068  	})
  4069  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  4070  
  4071  	p := &TestPlan{
  4072  		Options: UpdateOptions{Host: host},
  4073  	}
  4074  
  4075  	project := p.GetProject()
  4076  
  4077  	inputs = resource.PropertyMap{
  4078  		"a": resource.NewStringProperty("testA"),
  4079  		// b is missing
  4080  		"c": resource.MakeSecret(resource.NewStringProperty("testC")),
  4081  	}
  4082  
  4083  	// Run an update to create the resource and check we warn about b
  4084  	validate := func(
  4085  		project workspace.Project, target deploy.Target,
  4086  		entries JournalEntries, events []Event,
  4087  		res result.Result) result.Result {
  4088  
  4089  		if res != nil {
  4090  			return res
  4091  		}
  4092  
  4093  		for i := range events {
  4094  			if events[i].Type == "diag" {
  4095  				payload := events[i].Payload().(engine.DiagEventPayload)
  4096  				if payload.Severity == "warning" &&
  4097  					payload.URN == "urn:pulumi:test::test::pkgA:m:typA::resA" &&
  4098  					payload.Message == "<{%reset%}>Could not find property 'b' listed in additional secret outputs.<{%reset%}>\n" {
  4099  					// Found the message we expected
  4100  					return nil
  4101  				}
  4102  			}
  4103  		}
  4104  		return result.Error("Expected a diagnostic message, got none")
  4105  	}
  4106  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, validate)
  4107  	assert.Nil(t, res)
  4108  
  4109  	// Should have the provider and resA
  4110  	assert.Len(t, snap.Resources, 2)
  4111  	resA := snap.Resources[1]
  4112  	assert.Equal(t, []resource.PropertyKey{"a", "b"}, resA.AdditionalSecretOutputs)
  4113  	assert.True(t, resA.Outputs["a"].IsSecret())
  4114  	assert.True(t, resA.Outputs["c"].IsSecret())
  4115  }
  4116  
  4117  func TestDefaultParents(t *testing.T) {
  4118  	t.Parallel()
  4119  	t.Skipf("Default parents disabled due to https://github.com/pulumi/pulumi/issues/10950")
  4120  
  4121  	loaders := []*deploytest.ProviderLoader{
  4122  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  4123  			return &deploytest.Provider{
  4124  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  4125  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  4126  					return "created-id", news, resource.StatusOK, nil
  4127  				},
  4128  			}, nil
  4129  		}, deploytest.WithoutGrpc),
  4130  	}
  4131  
  4132  	program := deploytest.NewLanguageRuntime(func(info plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  4133  		_, _, _, err := monitor.RegisterResource(
  4134  			resource.RootStackType,
  4135  			info.Project+"-"+info.Stack,
  4136  			false,
  4137  			deploytest.ResourceOptions{})
  4138  		assert.NoError(t, err)
  4139  
  4140  		_, _, _, err = monitor.RegisterResource(
  4141  			"pkgA:m:typA",
  4142  			"resA",
  4143  			true,
  4144  			deploytest.ResourceOptions{})
  4145  		assert.NoError(t, err)
  4146  
  4147  		return nil
  4148  	})
  4149  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  4150  
  4151  	p := &TestPlan{
  4152  		Options: UpdateOptions{Host: host},
  4153  	}
  4154  
  4155  	project := p.GetProject()
  4156  
  4157  	// Run an update to create the resource
  4158  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  4159  	assert.Nil(t, res)
  4160  	assert.NotNil(t, snap)
  4161  	assert.Len(t, snap.Resources, 3)
  4162  
  4163  	// Assert that resource 0 is the stack
  4164  	assert.Equal(t, resource.RootStackType, snap.Resources[0].Type)
  4165  	// Assert that the other 2 resources have the stack as a parent
  4166  	assert.Equal(t, snap.Resources[0].URN, snap.Resources[1].Parent)
  4167  	assert.Equal(t, snap.Resources[0].URN, snap.Resources[2].Parent)
  4168  }
  4169  
  4170  func TestPendingDeleteOrder(t *testing.T) {
  4171  	// Test for https://github.com/pulumi/pulumi/issues/2948 Ensure that if we have resources A and B, and we
  4172  	// go to replace A but then fail to replace B that we correctly handle everything in the same order when
  4173  	// we retry the update.
  4174  	//
  4175  	// That is normally for this operation we would do the following:
  4176  	// 1. Create new A
  4177  	// 2. Create new B
  4178  	// 3. Delete old B
  4179  	// 4. Delete old A
  4180  	// So if step 2 fails to create the new B we want to see:
  4181  	// 1. Create new A
  4182  	// 2. Create new B (fail)
  4183  	// 1. Create new B
  4184  	// 2. Delete old B
  4185  	// 3. Delete old A
  4186  	// Currently (and what #2948 tracks) is that the engine does the following:
  4187  	// 1. Create new A
  4188  	// 2. Create new B (fail)
  4189  	// 3. Delete old A
  4190  	// 1. Create new B
  4191  	// 2. Delete old B
  4192  	// That delete A fails because the delete B needs to happen first.
  4193  
  4194  	t.Parallel()
  4195  
  4196  	cloudState := map[resource.ID]resource.PropertyMap{}
  4197  
  4198  	failCreationOfTypB := false
  4199  
  4200  	loaders := []*deploytest.ProviderLoader{
  4201  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  4202  			return &deploytest.Provider{
  4203  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  4204  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  4205  
  4206  					if strings.Contains(string(urn), "typB") && failCreationOfTypB {
  4207  						return "", nil, resource.StatusOK, fmt.Errorf("Could not create typB")
  4208  					}
  4209  
  4210  					id := resource.ID(fmt.Sprintf("%d", len(cloudState)))
  4211  					if !preview {
  4212  						cloudState[id] = news
  4213  					}
  4214  					return id, news, resource.StatusOK, nil
  4215  				},
  4216  				DeleteF: func(urn resource.URN,
  4217  					id resource.ID, olds resource.PropertyMap, timeout float64) (resource.Status, error) {
  4218  					// Fail if anything in cloud state still points to us
  4219  					for _, res := range cloudState {
  4220  						for _, v := range res {
  4221  							if v.IsString() && v.StringValue() == string(id) {
  4222  								return resource.StatusOK, fmt.Errorf("Can not delete %s", id)
  4223  							}
  4224  						}
  4225  					}
  4226  
  4227  					delete(cloudState, id)
  4228  					return resource.StatusOK, nil
  4229  				},
  4230  				DiffF: func(urn resource.URN,
  4231  					id resource.ID, olds, news resource.PropertyMap, ignoreChanges []string) (plugin.DiffResult, error) {
  4232  					if strings.Contains(string(urn), "typA") {
  4233  						if !olds["foo"].DeepEquals(news["foo"]) {
  4234  							return plugin.DiffResult{
  4235  								Changes:     plugin.DiffSome,
  4236  								ReplaceKeys: []resource.PropertyKey{"foo"},
  4237  								DetailedDiff: map[string]plugin.PropertyDiff{
  4238  									"foo": {
  4239  										Kind:      plugin.DiffUpdateReplace,
  4240  										InputDiff: true,
  4241  									},
  4242  								},
  4243  								DeleteBeforeReplace: false,
  4244  							}, nil
  4245  						}
  4246  					}
  4247  					if strings.Contains(string(urn), "typB") {
  4248  						if !olds["parent"].DeepEquals(news["parent"]) {
  4249  							return plugin.DiffResult{
  4250  								Changes:     plugin.DiffSome,
  4251  								ReplaceKeys: []resource.PropertyKey{"parent"},
  4252  								DetailedDiff: map[string]plugin.PropertyDiff{
  4253  									"parent": {
  4254  										Kind:      plugin.DiffUpdateReplace,
  4255  										InputDiff: true,
  4256  									},
  4257  								},
  4258  								DeleteBeforeReplace: false,
  4259  							}, nil
  4260  						}
  4261  					}
  4262  
  4263  					return plugin.DiffResult{}, nil
  4264  				},
  4265  				UpdateF: func(urn resource.URN,
  4266  					id resource.ID, olds, news resource.PropertyMap, timeout float64,
  4267  					ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
  4268  					assert.Fail(t, "Didn't expect update to be called")
  4269  					return nil, resource.StatusOK, nil
  4270  				},
  4271  			}, nil
  4272  		}, deploytest.WithoutGrpc),
  4273  	}
  4274  
  4275  	ins := resource.NewPropertyMapFromMap(map[string]interface{}{
  4276  		"foo": "bar",
  4277  	})
  4278  	program := deploytest.NewLanguageRuntime(func(info plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  4279  		_, idA, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{
  4280  			Inputs: ins,
  4281  		})
  4282  		assert.NoError(t, err)
  4283  
  4284  		_, _, _, err = monitor.RegisterResource("pkgA:m:typB", "resB", true, deploytest.ResourceOptions{
  4285  			Inputs: resource.NewPropertyMapFromMap(map[string]interface{}{
  4286  				"parent": idA,
  4287  			}),
  4288  		})
  4289  		assert.NoError(t, err)
  4290  
  4291  		return nil
  4292  	})
  4293  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  4294  
  4295  	p := &TestPlan{
  4296  		Options: UpdateOptions{Host: host},
  4297  	}
  4298  
  4299  	project := p.GetProject()
  4300  
  4301  	// Run an update to create the resources
  4302  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  4303  	assert.Nil(t, res)
  4304  	assert.NotNil(t, snap)
  4305  	assert.Len(t, snap.Resources, 3)
  4306  
  4307  	// Trigger a replacement of A but fail to create B
  4308  	failCreationOfTypB = true
  4309  	ins = resource.NewPropertyMapFromMap(map[string]interface{}{
  4310  		"foo": "baz",
  4311  	})
  4312  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  4313  	// Assert that this fails, we should have two copies of A now, one new one and one old one pending delete
  4314  	assert.NotNil(t, res)
  4315  	assert.NotNil(t, snap)
  4316  	assert.Len(t, snap.Resources, 4)
  4317  	assert.Equal(t, snap.Resources[1].Type, tokens.Type("pkgA:m:typA"))
  4318  	assert.False(t, snap.Resources[1].Delete)
  4319  	assert.Equal(t, snap.Resources[2].Type, tokens.Type("pkgA:m:typA"))
  4320  	assert.True(t, snap.Resources[2].Delete)
  4321  
  4322  	// Now allow B to create and try again
  4323  	failCreationOfTypB = false
  4324  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  4325  	assert.Nil(t, res)
  4326  	assert.NotNil(t, snap)
  4327  	assert.Len(t, snap.Resources, 3)
  4328  }
  4329  
  4330  func TestDuplicatesDueToAliases(t *testing.T) {
  4331  	t.Parallel()
  4332  
  4333  	// This is a test for https://github.com/pulumi/pulumi/issues/11173
  4334  	// to check that we don't allow resource aliases to refer to other resources.
  4335  	// That is if you have A, then try and add B saying it's alias is A we should error that's a duplicate.
  4336  	// We need to be careful that we handle this regardless of the order we send the RegisterResource requests for A and B.
  4337  
  4338  	loaders := []*deploytest.ProviderLoader{
  4339  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
  4340  			return &deploytest.Provider{
  4341  				CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
  4342  					preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
  4343  					return "created-id", news, resource.StatusOK, nil
  4344  				},
  4345  			}, nil
  4346  		}, deploytest.WithoutGrpc),
  4347  	}
  4348  
  4349  	mode := 0
  4350  	program := deploytest.NewLanguageRuntime(func(info plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
  4351  
  4352  		switch mode {
  4353  		case 0:
  4354  			// Default case, just make resA
  4355  			_, _, _, err := monitor.RegisterResource(
  4356  				"pkgA:m:typA",
  4357  				"resA",
  4358  				true,
  4359  				deploytest.ResourceOptions{})
  4360  			assert.NoError(t, err)
  4361  
  4362  		case 1:
  4363  			// First test case, try and create a new B that aliases to A. First make the A like normal...
  4364  			_, _, _, err := monitor.RegisterResource(
  4365  				"pkgA:m:typA",
  4366  				"resA",
  4367  				true,
  4368  				deploytest.ResourceOptions{})
  4369  			assert.NoError(t, err)
  4370  
  4371  			// ... then make B with an alias, it should error
  4372  			_, _, _, err = monitor.RegisterResource(
  4373  				"pkgA:m:typA",
  4374  				"resB",
  4375  				true,
  4376  				deploytest.ResourceOptions{
  4377  					Aliases: []resource.Alias{{Name: "resA"}},
  4378  				})
  4379  			assert.Error(t, err)
  4380  
  4381  		case 2:
  4382  			// Second test case, try and create a new B that aliases to A. First make the B with an alias...
  4383  			_, _, _, err := monitor.RegisterResource(
  4384  				"pkgA:m:typA",
  4385  				"resB",
  4386  				true,
  4387  				deploytest.ResourceOptions{
  4388  					Aliases: []resource.Alias{{Name: "resA"}},
  4389  				})
  4390  			assert.NoError(t, err)
  4391  
  4392  			// ... then try to make the A like normal. It should error that it's already been aliased away
  4393  			_, _, _, err = monitor.RegisterResource(
  4394  				"pkgA:m:typA",
  4395  				"resA",
  4396  				true,
  4397  				deploytest.ResourceOptions{})
  4398  			assert.Error(t, err)
  4399  		}
  4400  		return nil
  4401  	})
  4402  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
  4403  
  4404  	p := &TestPlan{
  4405  		Options: UpdateOptions{Host: host},
  4406  	}
  4407  
  4408  	project := p.GetProject()
  4409  
  4410  	// Run an update to create the starting A resource
  4411  	snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
  4412  	assert.Nil(t, res)
  4413  	assert.NotNil(t, snap)
  4414  	assert.Len(t, snap.Resources, 2)
  4415  	assert.Equal(t, resource.URN("urn:pulumi:test::test::pkgA:m:typA::resA"), snap.Resources[1].URN)
  4416  
  4417  	// Set mode to try and create A then a B that aliases to it, this should fail
  4418  	mode = 1
  4419  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  4420  	assert.NotNil(t, res)
  4421  	assert.NotNil(t, snap)
  4422  	assert.Len(t, snap.Resources, 2)
  4423  	assert.Equal(t, resource.URN("urn:pulumi:test::test::pkgA:m:typA::resA"), snap.Resources[1].URN)
  4424  
  4425  	// Set mode to try and create B first then a A, this should fail
  4426  	mode = 2
  4427  	snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
  4428  	assert.NotNil(t, res)
  4429  	assert.NotNil(t, snap)
  4430  	assert.Len(t, snap.Resources, 2)
  4431  	// Because we made the B first that's what should end up in the state file
  4432  	assert.Equal(t, resource.URN("urn:pulumi:test::test::pkgA:m:typA::resB"), snap.Resources[1].URN)
  4433  }