diff --git a/bin/configure-fastly.js b/bin/configure-fastly.js index c6f76411d..c3eb4cca2 100644 --- a/bin/configure-fastly.js +++ b/bin/configure-fastly.js @@ -24,7 +24,7 @@ var routes = routeJson.map(function (route) { async.auto({ version: function (cb) { - fastly.getLatestVersion(function (err, response) { + fastly.getLatestActiveVersion(function (err, response) { if (err) return cb(err); // Validate latest version before continuing if (response.active || response.locked) { diff --git a/bin/lib/fastly-extended.js b/bin/lib/fastly-extended.js index 0e4467221..ffac40acc 100644 --- a/bin/lib/fastly-extended.js +++ b/bin/lib/fastly-extended.js @@ -24,11 +24,11 @@ module.exports = function (apiKey, serviceId) { }; /* - * getLatestVersion: Get the most recent version for the configured service + * getLatestActiveVersion: Get the most recent version for the configured service * * @param {callback} Callback with signature *err, latestVersion) */ - fastly.getLatestVersion = function (cb) { + fastly.getLatestActiveVersion = function (cb) { if (!this.serviceId) { return cb('Failed to get latest version. No serviceId configured'); } @@ -37,11 +37,15 @@ module.exports = function (apiKey, serviceId) { if (err) { return cb('Failed to fetch versions: ' + err); } - var latestVersion = versions.reduce(function (lateVersion, version) { - if (!lateVersion) return version; - if (version.number > lateVersion.number) return version; - return lateVersion; - }); + var latestVersion = versions.reduce((latestActiveSoFar, cur) => { + // if one of [latestActiveSoFar, cur] is active and the other isn't, + // return whichever is active. If both are not active, return + // latestActiveSoFar. + if (!cur || !cur.active) return latestActiveSoFar; + if (!latestActiveSoFar || !latestActiveSoFar.active) return cur; + // when both are active, prefer whichever has a higher version number. + return (cur.number > latestActiveSoFar.number) ? cur : latestActiveSoFar; + }, null); return cb(null, latestVersion); }); }; diff --git a/test/unit/lib/fastly-extended.test.js b/test/unit/lib/fastly-extended.test.js new file mode 100644 index 000000000..451902c16 --- /dev/null +++ b/test/unit/lib/fastly-extended.test.js @@ -0,0 +1,160 @@ +describe('fastly library', () => { + let mockedFastlyRequest = {}; + + jest.mock('fastly', () => (() => ({ + request: mockedFastlyRequest + }))); + const fastlyExtended = require('../../../bin/lib/fastly-extended'); // eslint-disable-line global-require + + test('getLatestActiveVersion returns largest active VCL number, ' + + 'when called with VCLs in sequential order', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: false + }, + { + number: 2, + active: false + }, + { + number: 3, + active: true + }, + { + number: 4, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 3, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns largest active VCL number, when called with VCLs in mixed up order', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 4, + active: false + }, + { + number: 1, + active: false + }, + { + number: 2, + active: true + }, + { + number: 3, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 2, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns null, when none of the VCL versions are active', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 4, + active: false + }, + { + number: 1, + active: false + }, + { + number: 2, + active: false + }, + { + number: 3, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual(null); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns largest active VCL number, ' + + 'when called with a single active VCL', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: true + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 1, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns null, when called with a single inactive VCL', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual(null); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); +});