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

     1  package lifecycletest
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/blang/semver"
     7  	"github.com/stretchr/testify/assert"
     8  
     9  	. "github.com/pulumi/pulumi/pkg/v3/engine"
    10  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
    11  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy/deploytest"
    12  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    13  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    14  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
    15  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    16  )
    17  
    18  func TestDestroyWithPendingDelete(t *testing.T) {
    19  	t.Parallel()
    20  
    21  	loaders := []*deploytest.ProviderLoader{
    22  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
    23  			return &deploytest.Provider{}, nil
    24  		}),
    25  	}
    26  	program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, _ *deploytest.ResourceMonitor) error {
    27  		return nil
    28  	})
    29  	host := deploytest.NewPluginHost(nil, nil, program, loaders...)
    30  
    31  	p := &TestPlan{
    32  		Options: UpdateOptions{Host: host},
    33  	}
    34  
    35  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
    36  
    37  	// Create an old snapshot with two copies of a resource that share a URN: one that is pending deletion and one
    38  	// that is not.
    39  	old := &deploy.Snapshot{
    40  		Resources: []*resource.State{
    41  			{
    42  				Type:    resURN.Type(),
    43  				URN:     resURN,
    44  				Custom:  true,
    45  				ID:      "1",
    46  				Inputs:  resource.PropertyMap{},
    47  				Outputs: resource.PropertyMap{},
    48  			},
    49  			{
    50  				Type:    resURN.Type(),
    51  				URN:     resURN,
    52  				Custom:  true,
    53  				ID:      "0",
    54  				Inputs:  resource.PropertyMap{},
    55  				Outputs: resource.PropertyMap{},
    56  				Delete:  true,
    57  			},
    58  		},
    59  	}
    60  
    61  	p.Steps = []TestStep{{
    62  		Op: Update,
    63  		Validate: func(_ workspace.Project, _ deploy.Target, entries JournalEntries,
    64  			_ []Event, res result.Result) result.Result {
    65  
    66  			// Verify that we see a DeleteReplacement for the resource with ID 0 and a Delete for the resource with
    67  			// ID 1.
    68  			deletedID0, deletedID1 := false, false
    69  			for _, entry := range entries {
    70  				// Ignore non-terminal steps and steps that affect the injected default provider.
    71  				if entry.Kind != JournalEntrySuccess || entry.Step.URN() != resURN ||
    72  					(entry.Step.Op() != deploy.OpDelete && entry.Step.Op() != deploy.OpDeleteReplaced) {
    73  					continue
    74  				}
    75  
    76  				switch id := entry.Step.Old().ID; id {
    77  				case "0":
    78  					assert.False(t, deletedID0)
    79  					deletedID0 = true
    80  				case "1":
    81  					assert.False(t, deletedID1)
    82  					deletedID1 = true
    83  				default:
    84  					assert.Fail(t, "unexpected resource ID %v", string(id))
    85  				}
    86  			}
    87  			assert.True(t, deletedID0)
    88  			assert.True(t, deletedID1)
    89  
    90  			return res
    91  		},
    92  	}}
    93  	p.Run(t, old)
    94  }
    95  
    96  func TestUpdateWithPendingDelete(t *testing.T) {
    97  	t.Parallel()
    98  
    99  	loaders := []*deploytest.ProviderLoader{
   100  		deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
   101  			return &deploytest.Provider{}, nil
   102  		}),
   103  	}
   104  
   105  	host := deploytest.NewPluginHost(nil, nil, nil, loaders...)
   106  
   107  	p := &TestPlan{
   108  		Options: UpdateOptions{Host: host},
   109  	}
   110  
   111  	resURN := p.NewURN("pkgA:m:typA", "resA", "")
   112  
   113  	// Create an old snapshot with two copies of a resource that share a URN: one that is pending deletion and one
   114  	// that is not.
   115  	old := &deploy.Snapshot{
   116  		Resources: []*resource.State{
   117  			{
   118  				Type:    resURN.Type(),
   119  				URN:     resURN,
   120  				Custom:  true,
   121  				ID:      "1",
   122  				Inputs:  resource.PropertyMap{},
   123  				Outputs: resource.PropertyMap{},
   124  			},
   125  			{
   126  				Type:    resURN.Type(),
   127  				URN:     resURN,
   128  				Custom:  true,
   129  				ID:      "0",
   130  				Inputs:  resource.PropertyMap{},
   131  				Outputs: resource.PropertyMap{},
   132  				Delete:  true,
   133  			},
   134  		},
   135  	}
   136  
   137  	p.Steps = []TestStep{{
   138  		Op: Destroy,
   139  		Validate: func(_ workspace.Project, _ deploy.Target, entries JournalEntries,
   140  			_ []Event, res result.Result) result.Result {
   141  
   142  			// Verify that we see a DeleteReplacement for the resource with ID 0 and a Delete for the resource with
   143  			// ID 1.
   144  			deletedID0, deletedID1 := false, false
   145  			for _, entry := range entries {
   146  				// Ignore non-terminal steps and steps that affect the injected default provider.
   147  				if entry.Kind != JournalEntrySuccess || entry.Step.URN() != resURN ||
   148  					(entry.Step.Op() != deploy.OpDelete && entry.Step.Op() != deploy.OpDeleteReplaced) {
   149  					continue
   150  				}
   151  
   152  				switch id := entry.Step.Old().ID; id {
   153  				case "0":
   154  					assert.False(t, deletedID0)
   155  					deletedID0 = true
   156  				case "1":
   157  					assert.False(t, deletedID1)
   158  					deletedID1 = true
   159  				default:
   160  					assert.Fail(t, "unexpected resource ID %v", string(id))
   161  				}
   162  			}
   163  			assert.True(t, deletedID0)
   164  			assert.True(t, deletedID1)
   165  
   166  			return res
   167  		},
   168  	}}
   169  	p.Run(t, old)
   170  }