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 }