From f7b2d7374e791406ae5aa599a76999c01810b2c1 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 17 Jan 2025 14:03:28 +0100 Subject: [PATCH 1/4] chore: reduce the runtime of CLI unit tests by 2 minutes There was a test that was waiting a minute for some operation to time out after 7 retries. Make those retries happen faster by hijacking the timer system. --- ...sync-mapping-templates-hotswap-deployments.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts index 46e03cc88aba2..b90be33f5b167 100644 --- a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts @@ -1045,8 +1045,15 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot ); silentTest( - 'updateFunction() API fails if it recieves 7 failed attempts in a row - this is a long running test', + 'updateFunction() API fails if it recieves 7 failed attempts in a row', async () => { + const realSetTimeout = setTimeout; + jest.useFakeTimers(); + + // Ignore the wait times that the SDK tries to impose and always set timers for 1 ms + jest.spyOn(global, 'setTimeout').mockImplementation((fn) => { + return realSetTimeout(fn, 1); + }); // GIVEN mockAppSyncClient @@ -1123,6 +1130,8 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot requestMappingTemplate: '## original request template', responseMappingTemplate: '## new response template', }); + + jest.useRealTimers(); }, 320000, ); From f42070c515fbf0845a5b481516c5eab8c11e1078 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 17 Jan 2025 14:04:49 +0100 Subject: [PATCH 2/4] Add try/finally --- ...ping-templates-hotswap-deployments.test.ts | 130 +++++++++--------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts index b90be33f5b167..7c363fa8b59ed 100644 --- a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts @@ -1049,52 +1049,33 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot async () => { const realSetTimeout = setTimeout; jest.useFakeTimers(); - - // Ignore the wait times that the SDK tries to impose and always set timers for 1 ms - jest.spyOn(global, 'setTimeout').mockImplementation((fn) => { - return realSetTimeout(fn, 1); - }); - - // GIVEN - mockAppSyncClient - .on(ListFunctionsCommand) - .resolvesOnce({ - functions: [{ name: 'my-function', functionId: 'functionId' }], + try { + // Ignore the wait times that the SDK tries to impose and always set timers for 1 ms + jest.spyOn(global, 'setTimeout').mockImplementation((fn) => { + return realSetTimeout(fn, 1); }); - const ConcurrentModError = new Error('ConcurrentModificationException: Schema is currently being altered, please wait until that is complete.'); - ConcurrentModError.name = 'ConcurrentModificationException'; - mockAppSyncClient - .on(UpdateFunctionCommand) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .resolvesOnce({ functionConfiguration: { name: 'my-function', dataSourceName: 'my-datasource', functionId: 'functionId' } }); - - setup.setCurrentCfnStackTemplate({ - Resources: { - AppSyncFunction: { - Type: 'AWS::AppSync::FunctionConfiguration', - Properties: { - Name: 'my-function', - ApiId: 'apiId', - DataSourceName: 'my-datasource', - FunctionVersion: '2018-05-29', - RequestMappingTemplate: '## original request template', - ResponseMappingTemplate: '## original response template', - }, - Metadata: { - 'aws:asset:path': 'old-path', - }, - }, - }, - }); - const cdkStackArtifact = setup.cdkStackArtifactOf({ - template: { + // GIVEN + mockAppSyncClient + .on(ListFunctionsCommand) + .resolvesOnce({ + functions: [{ name: 'my-function', functionId: 'functionId' }], + }); + + const ConcurrentModError = new Error('ConcurrentModificationException: Schema is currently being altered, please wait until that is complete.'); + ConcurrentModError.name = 'ConcurrentModificationException'; + mockAppSyncClient + .on(UpdateFunctionCommand) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .resolvesOnce({ functionConfiguration: { name: 'my-function', dataSourceName: 'my-datasource', functionId: 'functionId' } }); + + setup.setCurrentCfnStackTemplate({ Resources: { AppSyncFunction: { Type: 'AWS::AppSync::FunctionConfiguration', @@ -1104,34 +1085,55 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot DataSourceName: 'my-datasource', FunctionVersion: '2018-05-29', RequestMappingTemplate: '## original request template', - ResponseMappingTemplate: '## new response template', + ResponseMappingTemplate: '## original response template', }, Metadata: { - 'aws:asset:path': 'new-path', + 'aws:asset:path': 'old-path', }, }, }, - }, - }); + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + AppSyncFunction: { + Type: 'AWS::AppSync::FunctionConfiguration', + Properties: { + Name: 'my-function', + ApiId: 'apiId', + DataSourceName: 'my-datasource', + FunctionVersion: '2018-05-29', + RequestMappingTemplate: '## original request template', + ResponseMappingTemplate: '## new response template', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); - // WHEN - await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( - 'ConcurrentModificationException', - ); + // WHEN + await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( + 'ConcurrentModificationException', + ); - // THEN - expect(mockAppSyncClient).toHaveReceivedCommandTimes(UpdateFunctionCommand, 7); // 1st attempt and then 6 retries before bailing - expect(mockAppSyncClient).toHaveReceivedCommandWith(UpdateFunctionCommand, { - apiId: 'apiId', - dataSourceName: 'my-datasource', - functionId: 'functionId', - functionVersion: '2018-05-29', - name: 'my-function', - requestMappingTemplate: '## original request template', - responseMappingTemplate: '## new response template', - }); + // THEN + expect(mockAppSyncClient).toHaveReceivedCommandTimes(UpdateFunctionCommand, 7); // 1st attempt and then 6 retries before bailing + expect(mockAppSyncClient).toHaveReceivedCommandWith(UpdateFunctionCommand, { + apiId: 'apiId', + dataSourceName: 'my-datasource', + functionId: 'functionId', + functionVersion: '2018-05-29', + name: 'my-function', + requestMappingTemplate: '## original request template', + responseMappingTemplate: '## new response template', + }); - jest.useRealTimers(); + } finally { + jest.useRealTimers(); + } }, 320000, ); From 8bc2664fedc2be85336113941cadf12a1dfbc63d Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 17 Jan 2025 15:23:36 +0100 Subject: [PATCH 3/4] Nicer solution --- ...ping-templates-hotswap-deployments.test.ts | 136 +++++++++--------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts index 7c363fa8b59ed..36ac5798a4860 100644 --- a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts @@ -1047,35 +1047,52 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot silentTest( 'updateFunction() API fails if it recieves 7 failed attempts in a row', async () => { + // Ignore the wait times that the SDK tries to impose and always set timers for 1 ms const realSetTimeout = setTimeout; - jest.useFakeTimers(); - try { - // Ignore the wait times that the SDK tries to impose and always set timers for 1 ms - jest.spyOn(global, 'setTimeout').mockImplementation((fn) => { - return realSetTimeout(fn, 1); + const mockSetTimeout = jest.spyOn(global, 'setTimeout').mockImplementation((fn) => { + return realSetTimeout(fn, 1); + }); + + // GIVEN + mockAppSyncClient + .on(ListFunctionsCommand) + .resolvesOnce({ + functions: [{ name: 'my-function', functionId: 'functionId' }], }); - // GIVEN - mockAppSyncClient - .on(ListFunctionsCommand) - .resolvesOnce({ - functions: [{ name: 'my-function', functionId: 'functionId' }], - }); - - const ConcurrentModError = new Error('ConcurrentModificationException: Schema is currently being altered, please wait until that is complete.'); - ConcurrentModError.name = 'ConcurrentModificationException'; - mockAppSyncClient - .on(UpdateFunctionCommand) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .rejectsOnce(ConcurrentModError) - .resolvesOnce({ functionConfiguration: { name: 'my-function', dataSourceName: 'my-datasource', functionId: 'functionId' } }); - - setup.setCurrentCfnStackTemplate({ + const ConcurrentModError = new Error('ConcurrentModificationException: Schema is currently being altered, please wait until that is complete.'); + ConcurrentModError.name = 'ConcurrentModificationException'; + mockAppSyncClient + .on(UpdateFunctionCommand) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .rejectsOnce(ConcurrentModError) + .resolvesOnce({ functionConfiguration: { name: 'my-function', dataSourceName: 'my-datasource', functionId: 'functionId' } }); + + setup.setCurrentCfnStackTemplate({ + Resources: { + AppSyncFunction: { + Type: 'AWS::AppSync::FunctionConfiguration', + Properties: { + Name: 'my-function', + ApiId: 'apiId', + DataSourceName: 'my-datasource', + FunctionVersion: '2018-05-29', + RequestMappingTemplate: '## original request template', + ResponseMappingTemplate: '## original response template', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { Resources: { AppSyncFunction: { Type: 'AWS::AppSync::FunctionConfiguration', @@ -1085,55 +1102,40 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot DataSourceName: 'my-datasource', FunctionVersion: '2018-05-29', RequestMappingTemplate: '## original request template', - ResponseMappingTemplate: '## original response template', + ResponseMappingTemplate: '## new response template', }, Metadata: { - 'aws:asset:path': 'old-path', - }, - }, - }, - }); - const cdkStackArtifact = setup.cdkStackArtifactOf({ - template: { - Resources: { - AppSyncFunction: { - Type: 'AWS::AppSync::FunctionConfiguration', - Properties: { - Name: 'my-function', - ApiId: 'apiId', - DataSourceName: 'my-datasource', - FunctionVersion: '2018-05-29', - RequestMappingTemplate: '## original request template', - ResponseMappingTemplate: '## new response template', - }, - Metadata: { - 'aws:asset:path': 'new-path', - }, + 'aws:asset:path': 'new-path', }, }, }, - }); - - // WHEN - await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( - 'ConcurrentModificationException', - ); + }, + }); - // THEN - expect(mockAppSyncClient).toHaveReceivedCommandTimes(UpdateFunctionCommand, 7); // 1st attempt and then 6 retries before bailing - expect(mockAppSyncClient).toHaveReceivedCommandWith(UpdateFunctionCommand, { - apiId: 'apiId', - dataSourceName: 'my-datasource', - functionId: 'functionId', - functionVersion: '2018-05-29', - name: 'my-function', - requestMappingTemplate: '## original request template', - responseMappingTemplate: '## new response template', - }); + // WHEN + const expectation = expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( + 'ConcurrentModificationException', + ); - } finally { - jest.useRealTimers(); + for (let i = 0; i < 1000; i++) { + jest.advanceTimersByTime(1000); } + + await expectation; + + // THEN + expect(mockAppSyncClient).toHaveReceivedCommandTimes(UpdateFunctionCommand, 7); // 1st attempt and then 6 retries before bailing + expect(mockAppSyncClient).toHaveReceivedCommandWith(UpdateFunctionCommand, { + apiId: 'apiId', + dataSourceName: 'my-datasource', + functionId: 'functionId', + functionVersion: '2018-05-29', + name: 'my-function', + requestMappingTemplate: '## original request template', + responseMappingTemplate: '## new response template', + }); + + mockSetTimeout.mockRestore(); }, 320000, ); From 1bbdefd93faa30ac4cbed09f1f78d34f0144508b Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 17 Jan 2025 15:27:06 +0100 Subject: [PATCH 4/4] Don't need this anymore --- .../appsync-mapping-templates-hotswap-deployments.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts index 36ac5798a4860..f4c89d634f99f 100644 --- a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts @@ -1113,16 +1113,10 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }); // WHEN - const expectation = expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( + await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow( 'ConcurrentModificationException', ); - for (let i = 0; i < 1000; i++) { - jest.advanceTimersByTime(1000); - } - - await expectation; - // THEN expect(mockAppSyncClient).toHaveReceivedCommandTimes(UpdateFunctionCommand, 7); // 1st attempt and then 6 retries before bailing expect(mockAppSyncClient).toHaveReceivedCommandWith(UpdateFunctionCommand, {