11import { readFileSync } from "node:fs"
22import { dirname , join } from "node:path"
33import { fileURLToPath } from "node:url"
4- import { describe , expect , test } from "vitest"
5- import { checkDestructiveChanges } from "../../src/changesets/checkDestructiveChanges"
4+ import {
5+ beforeEach ,
6+ describe ,
7+ expect ,
8+ test ,
9+ vi
10+ } from "vitest"
11+ import { checkDestructiveChanges , checkDestructiveChangeSet } from "../../src/changesets/checkDestructiveChanges"
12+
13+ const mockCloudFormationSend = vi . fn ( )
14+
15+ vi . mock ( "@aws-sdk/client-cloudformation" , ( ) => {
16+ class CloudFormationClient {
17+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18+ config : any
19+ constructor ( config : { region : string } ) {
20+ this . config = config
21+ }
22+
23+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24+ send ( command : any ) {
25+ return mockCloudFormationSend ( command )
26+ }
27+ }
28+
29+ class DescribeChangeSetCommand {
30+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31+ input : any
32+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33+ constructor ( input : any ) {
34+ this . input = input
35+ }
36+ }
37+
38+ return { CloudFormationClient, DescribeChangeSetCommand}
39+ } )
640
741const __filename = fileURLToPath ( import . meta. url )
842const __dirname = dirname ( __filename )
@@ -13,6 +47,10 @@ const loadChangeSet = (filePath: string) => JSON.parse(readFileSync(filePath, "u
1347const destructiveChangeSet = loadChangeSet ( join ( fixturesDir , "destructive_changeset.json" ) )
1448const safeChangeSet = loadChangeSet ( join ( fixturesDir , "safe_changeset.json" ) )
1549
50+ beforeEach ( ( ) => {
51+ mockCloudFormationSend . mockReset ( )
52+ } )
53+
1654describe ( "checkDestructiveChanges" , ( ) => {
1755 test ( "returns resources that require replacement" , ( ) => {
1856 const replacements = checkDestructiveChanges ( destructiveChangeSet )
@@ -58,3 +96,44 @@ describe("checkDestructiveChanges", () => {
5896 ] )
5997 } )
6098} )
99+
100+ describe ( "checkDestructiveChangeSet" , ( ) => {
101+ test ( "logs success when no destructive changes are present" , async ( ) => {
102+ mockCloudFormationSend . mockResolvedValueOnce ( safeChangeSet )
103+
104+ const logSpy = vi . spyOn ( console , "log" ) . mockImplementation ( ( ) => undefined )
105+ const errorSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => undefined )
106+
107+ try {
108+ await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" ) ) . resolves . toBeUndefined ( )
109+
110+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
111+ const command = mockCloudFormationSend . mock . calls [ 0 ] [ 0 ] as { input : { ChangeSetName : string ; StackName : string } }
112+ expect ( command . input ) . toEqual ( { ChangeSetName : "cs" , StackName : "stack" } )
113+ expect ( logSpy ) . toHaveBeenCalledWith ( "Change set cs for stack stack has no destructive changes." )
114+ expect ( errorSpy ) . not . toHaveBeenCalled ( )
115+ } finally {
116+ logSpy . mockRestore ( )
117+ errorSpy . mockRestore ( )
118+ }
119+ } )
120+
121+ test ( "logs details and throws when destructive changes exist" , async ( ) => {
122+ mockCloudFormationSend . mockResolvedValueOnce ( destructiveChangeSet )
123+
124+ const logSpy = vi . spyOn ( console , "log" ) . mockImplementation ( ( ) => undefined )
125+ const errorSpy = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => undefined )
126+
127+ try {
128+ await expect ( checkDestructiveChangeSet ( "cs" , "stack" , "eu-west-2" ) )
129+ . rejects . toThrow ( "Change set cs contains destructive changes" )
130+
131+ expect ( mockCloudFormationSend ) . toHaveBeenCalledTimes ( 1 )
132+ expect ( logSpy ) . not . toHaveBeenCalled ( )
133+ expect ( errorSpy ) . toHaveBeenCalledWith ( "Resources that require attention:" )
134+ } finally {
135+ logSpy . mockRestore ( )
136+ errorSpy . mockRestore ( )
137+ }
138+ } )
139+ } )
0 commit comments