Skip to content

Commit dfcfe3f

Browse files
DianaIonitaCopilot
andauthored
Tests for applyUpdateStageForChunk (#143)
* Tests for applyUpdateStageForChunk * Update src/stageCache.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent da52e73 commit dfcfe3f

8 files changed

+329
-20
lines changed

package-lock.json

Lines changed: 184 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@
3030
},
3131
"devDependencies": {
3232
"chai": "^4.1.2",
33+
"chai-as-promised": "^8.0.1",
3334
"chance": "^1.0.16",
3435
"lodash.split": "^4.4.0",
3536
"mocha": "^10.0.0",
3637
"mocha-junit-reporter": "^2.0.2",
37-
"proxyquire": "^2.1.0"
38+
"proxyquire": "^2.1.0",
39+
"sinon": "^20.0.0"
3840
}
3941
}

src/apiGatewayCachingPlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const ApiGatewayCachingSettings = require('./ApiGatewayCachingSettings');
44
const cacheKeyParameters = require('./cacheKeyParameters');
5-
const updateStageCacheSettings = require('./stageCache');
5+
const { updateStageCacheSettings } = require('./stageCache');
66
const { restApiExists, outputRestApiIdTo } = require('./restApiId');
77

88
class ApiGatewayCachingPlugin {

src/stageCache.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const isEmpty = require('lodash.isempty');
22
const { retrieveRestApiId } = require('./restApiId');
33
const MAX_PATCH_OPERATIONS_PER_STAGE_UPDATE = 80;
4+
const BASE_RETRY_DELAY_MS = 500;
45

56
String.prototype.replaceAll = function (search, replacement) {
67
let target = this;
@@ -194,38 +195,48 @@ const updateStageFor = async (serverless, params, stage, region) => {
194195
paramsInChunks.push(params);
195196
}
196197

197-
for (let index in paramsInChunks) {
198-
serverless.cli.log(`[serverless-api-gateway-caching] Updating API Gateway cache settings (${parseInt(index) + 1} of ${paramsInChunks.length}).`);
199-
await applyUpdateStageForChunk(paramsInChunks[index], serverless, stage, region);
198+
for (const [index, chunk] of paramsInChunks.entries()) {
199+
serverless.cli.log(`[serverless-api-gateway-caching] Updating API Gateway cache settings (${index + 1} of ${paramsInChunks.length}).`);
200+
await applyUpdateStageForChunk(chunk, serverless, stage, region);
200201
}
201202

202203
serverless.cli.log(`[serverless-api-gateway-caching] Done updating API Gateway cache settings.`);
203204
}
204205

205206
const applyUpdateStageForChunk = async (chunk, serverless, stage, region) => {
206207
const maxRetries = 10;
207-
const baseDelay = 500;
208+
const baseDelay = BASE_RETRY_DELAY_MS;
208209
let attempt = 0;
209210

210-
while (attempt <= maxRetries) {
211+
while (attempt < maxRetries) {
211212
try {
212213
serverless.cli.log(`[serverless-api-gateway-caching] Updating API Gateway cache settings. Attempt ${attempt + 1}.`);
213214
await serverless.providers.aws.request('APIGateway', 'updateStage', chunk, stage, region);
214-
break;
215+
return;
215216
} catch (error) {
217+
// Check for specific error code first, fallback to message check
216218
if (
217-
attempt < maxRetries &&
218-
error.message.includes('A previous change is still in progress')
219+
(error.code === 'ConflictException' || error.message.includes('A previous change is still in progress'))
219220
) {
220221
attempt++;
221-
const delay = baseDelay * 2 ** (attempt - 1);
222+
if (attempt >= maxRetries) {
223+
serverless.cli.log(`[serverless-api-gateway-caching] Maximum retries (${maxRetries}) reached. Failed to update API Gateway cache settings.`);
224+
// Log the full error for better debugging before throwing
225+
console.error('[serverless-api-gateway-caching] Final error details:', error);
226+
throw new Error(`Failed to update API Gateway cache settings after ${maxRetries} retries: ${error.message}`);
227+
}
228+
const delay = baseDelay * 2 ** attempt;
222229
serverless.cli.log(`[serverless-api-gateway-caching] Retrying (${attempt}/${maxRetries}) after ${delay}ms due to error: ${error.message}`);
223230
await new Promise((resolve) => setTimeout(resolve, delay));
224231
} else {
225-
throw new Error(`Failed to update API Gateway cache settings after ${attempt} retries: ${error.message}`);
232+
console.error('[serverless-api-gateway-caching] Non-retryable error during update:', error);
233+
// Throw immediately for non-retryable errors or if string/code doesn't match
234+
throw new Error(`Failed to update API Gateway cache settings: ${error.message}`);
226235
}
227236
}
228237
}
238+
// This part should ideally not be reached if the loop condition is < maxRetries and success returns early.
239+
// Removing the final safeguard as it is redundant.
229240
}
230241

231242
const updateStageCacheSettings = async (settings, serverless) => {
@@ -266,4 +277,7 @@ const updateStageCacheSettings = async (settings, serverless) => {
266277
await updateStageFor(serverless, params, settings.stage, settings.region);
267278
}
268279

269-
module.exports = updateStageCacheSettings;
280+
module.exports = {
281+
updateStageCacheSettings,
282+
applyUpdateStageForChunk
283+
}

test/creating-plugin.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,16 @@ describe('Creating plugin', () => {
9090
const serverless = { cli: { log: (message) => { logCalledWith = message } } };
9191
const restApiIdStub = {
9292
restApiExists: () => scenario.thereIsARestApi,
93-
outputRestApiIdTo: () => outputRestApiIdCalled = true
93+
outputRestApiIdTo: () => {}
94+
};
95+
const stageCacheStub = {
96+
updateStageCacheSettings: () => updateStageCacheSettingsCalled = true
9497
};
95-
const stageCacheStub = () => updateStageCacheSettingsCalled = true;
9698
const ApiGatewayCachingPlugin = proxyquire('../src/apiGatewayCachingPlugin', { './restApiId': restApiIdStub, './stageCache': stageCacheStub });
9799
const plugin = new ApiGatewayCachingPlugin(serverless, {});
98100

99-
before(() => {
100-
plugin.updateStage();
101+
before(async () => {
102+
await plugin.updateStage();
101103
});
102104

103105
it('should log a message', () => {

test/steps/when.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const updateStageCacheSettings = require('../../src/stageCache');
1+
const { updateStageCacheSettings } = require('../../src/stageCache');
22

33
const updating_stage_cache_settings = async (settings, serverless) => {
44
return await updateStageCacheSettings(settings, serverless);

test/updating-stage-cache-settings-for-additional-endpoints.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const given = require('../test/steps/given');
22
const when = require('../test/steps/when');
33
const ApiGatewayCachingSettings = require('../src/ApiGatewayCachingSettings');
4-
const updateStageCacheSettings = require('../src/stageCache');
4+
const { updateStageCacheSettings } = require('../src/stageCache');
55
const expect = require('chai').expect;
66

77
describe('Updating stage cache settings for additional endpoints defined as CloudFormation', () => {

0 commit comments

Comments
 (0)