@@ -40,7 +40,16 @@ vi.mock("@aws-sdk/client-cloudformation", () => {
4040 }
4141 }
4242
43- return { CloudFormationClient, DescribeChangeSetCommand}
43+ class DescribeStacksCommand {
44+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45+ input : any
46+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47+ constructor ( input : any ) {
48+ this . input = input
49+ }
50+ }
51+
52+ return { CloudFormationClient, DescribeChangeSetCommand, DescribeStacksCommand}
4453} )
4554
4655const __filename = fileURLToPath ( import . meta. url )
@@ -190,30 +199,42 @@ describe("checkDestructiveChanges", () => {
190199describe ( "checkDestructiveChangeSet" , ( ) => {
191200 const logSpy = vi . spyOn ( console , "log" ) . mockImplementation ( ( ) => undefined )
192201 const errorSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => undefined )
202+ const mockStackExists = ( ) => {
203+ mockCloudFormationSend . mockResolvedValueOnce ( {
204+ Stacks : [
205+ {
206+ StackName : "stack"
207+ }
208+ ]
209+ } )
210+ }
211+
193212 afterEach ( ( ) => {
194213 logSpy . mockReset ( )
195214 errorSpy . mockReset ( )
196215 } )
197216
198217 test ( "logs success when no destructive changes are present" , async ( ) => {
218+ mockStackExists ( )
199219 mockCloudFormationSend . mockResolvedValueOnce ( safeChangeSet )
200220
201221 await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" ) ) . resolves . toBeUndefined ( )
202222
203- expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
204- const command = mockCloudFormationSend . mock . calls [ 0 ] [ 0 ] as { input : { ChangeSetName : string ; StackName : string } }
223+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 2 )
224+ const command = mockCloudFormationSend . mock . calls [ 1 ] [ 0 ] as { input : { ChangeSetName : string ; StackName : string } }
205225 expect ( command . input ) . toEqual ( { ChangeSetName : "cs" , StackName : "stack" } )
206226 expect ( logSpy ) . toHaveBeenCalledWith ( "Change set cs for stack stack has no destructive changes that are not waived." )
207227 expect ( errorSpy ) . not . toHaveBeenCalled ( )
208228 } )
209229
210230 test ( "logs details and throws when destructive changes exist" , async ( ) => {
231+ mockStackExists ( )
211232 mockCloudFormationSend . mockResolvedValueOnce ( destructiveChangeSet )
212233
213234 await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" ) )
214235 . rejects . toThrow ( "Change set cs contains destructive changes" )
215236
216- expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
237+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 2 )
217238 expect ( logSpy ) . not . toHaveBeenCalled ( )
218239 expect ( errorSpy ) . toHaveBeenCalledWith ( "Resources that require attention:" )
219240 } )
@@ -234,6 +255,7 @@ describe("checkDestructiveChangeSet", () => {
234255 }
235256 ]
236257 }
258+ mockStackExists ( )
237259 mockCloudFormationSend . mockResolvedValueOnce ( changeSet )
238260
239261 const allowedChanges : Array < AllowedDestructiveChange > = [
@@ -253,7 +275,7 @@ describe("checkDestructiveChangeSet", () => {
253275 await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" , allowedChanges ) )
254276 . resolves . toBeUndefined ( )
255277
256- expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
278+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 2 )
257279 expect ( logSpy ) . toHaveBeenCalledWith ( expect . stringContaining ( "Allowing destructive change ResourceToRemove" ) )
258280 expect ( logSpy ) . toHaveBeenCalledWith ( "Change set cs for stack stack has no destructive changes that are not waived." )
259281 expect ( errorSpy ) . not . toHaveBeenCalled ( )
@@ -275,6 +297,7 @@ describe("checkDestructiveChangeSet", () => {
275297 }
276298 ]
277299 }
300+ mockStackExists ( )
278301 mockCloudFormationSend . mockResolvedValueOnce ( changeSet )
279302
280303 const allowedChanges : Array < AllowedDestructiveChange > = [
@@ -315,6 +338,7 @@ describe("checkDestructiveChangeSet", () => {
315338 }
316339 ]
317340 }
341+ mockStackExists ( )
318342 mockCloudFormationSend . mockResolvedValueOnce ( changeSet )
319343
320344 const allowedChanges : Array < AllowedDestructiveChange > = [
@@ -336,4 +360,17 @@ describe("checkDestructiveChangeSet", () => {
336360
337361 expect ( errorSpy ) . toHaveBeenCalledWith ( "Resources that require attention:" )
338362 } )
363+
364+ test ( "logs and exits without error when the stack does not exist" , async ( ) => {
365+ const stackMissingError = new Error ( "Stack with id stack does not exist" )
366+ stackMissingError . name = "ValidationError"
367+
368+ mockCloudFormationSend . mockRejectedValueOnce ( stackMissingError )
369+
370+ await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" ) ) . resolves . toBeUndefined ( )
371+
372+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
373+ expect ( logSpy ) . toHaveBeenCalledWith ( "Stack stack does not exist. Skipping destructive change check." )
374+ expect ( errorSpy ) . not . toHaveBeenCalled ( )
375+ } )
339376} )
0 commit comments