From b66f89a76dd4fd6467e028c9650c76889828ed43 Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Mon, 20 Feb 2023 09:45:06 +0000 Subject: [PATCH 1/6] feat: add retry method to refresh token on 403 At the moment if the backbone token secret changes the adapter can have a token it thinks is valid but which can no longer be decrypted by the backbone. On a 403 error this will trigger a new token grant and resend the request with the new token. --- dist/adapter.esm.js | 34 +++++++++++++++++++++++++++++----- dist/adapter.js | 34 +++++++++++++++++++++++++++++----- src/adapter/index.js | 34 +++++++++++++++++++++++++++++----- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/dist/adapter.esm.js b/dist/adapter.esm.js index 1acacf6..97b4e82 100644 --- a/dist/adapter.esm.js +++ b/dist/adapter.esm.js @@ -115,9 +115,10 @@ class Adapter { * Messages should be passed through encode before sending * @param {string} topic * @param {string} body + * @param {boolean} is_retry * @returns */ - publish(topic, body) { + publish(topic, body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -136,7 +137,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.publish(topic, body, true); + else return Promise.reject(error); }); } @@ -148,10 +160,11 @@ class Adapter { * quickly in an emergency scenario. * * Messages should be passed through encode before sending - * @param {*} body + * @param {string} body + * @param {boolean} is_retry * @returns */ - broadcast(body) { + broadcast(body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -169,7 +182,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.broadcast(body, true); + else return Promise.reject(error); }); } } diff --git a/dist/adapter.js b/dist/adapter.js index c868d51..57c294f 100644 --- a/dist/adapter.js +++ b/dist/adapter.js @@ -117,9 +117,10 @@ class Adapter { * Messages should be passed through encode before sending * @param {string} topic * @param {string} body + * @param {boolean} is_retry * @returns */ - publish(topic, body) { + publish(topic, body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -138,7 +139,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.publish(topic, body, true); + else return Promise.reject(error); }); } @@ -150,10 +162,11 @@ class Adapter { * quickly in an emergency scenario. * * Messages should be passed through encode before sending - * @param {*} body + * @param {string} body + * @param {boolean} is_retry * @returns */ - broadcast(body) { + broadcast(body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -171,7 +184,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.broadcast(body, true); + else return Promise.reject(error); }); } } diff --git a/src/adapter/index.js b/src/adapter/index.js index d52101f..51ecde7 100644 --- a/src/adapter/index.js +++ b/src/adapter/index.js @@ -115,9 +115,10 @@ export class Adapter { * Messages should be passed through encode before sending * @param {string} topic * @param {string} body + * @param {boolean} is_retry * @returns */ - publish(topic, body) { + publish(topic, body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -136,7 +137,18 @@ export class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.publish(topic, body, true); + else return Promise.reject(error); }); } @@ -148,10 +160,11 @@ export class Adapter { * quickly in an emergency scenario. * * Messages should be passed through encode before sending - * @param {*} body + * @param {string} body + * @param {boolean} is_retry * @returns */ - broadcast(body) { + broadcast(body, is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -169,7 +182,18 @@ export class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status_code) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.broadcast(body, true); + else return Promise.reject(error); }); } } -- GitLab From 341e40d075abb536a9c752bb833116b761edc053 Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Mon, 20 Feb 2023 10:15:05 +0000 Subject: [PATCH 2/6] fix: correct reference to error status code + add catch and retry in poll method --- dist/adapter.esm.js | 22 +++++++++++++++++----- dist/adapter.js | 22 +++++++++++++++++----- src/adapter/index.js | 22 +++++++++++++++++----- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/dist/adapter.esm.js b/dist/adapter.esm.js index 97b4e82..cfb0379 100644 --- a/dist/adapter.esm.js +++ b/dist/adapter.esm.js @@ -82,10 +82,11 @@ class Adapter { /** * Call the GET /receive endpoint and process the messages with decode * - * Returns the response + * Returns the response + * @param {boolean} is_retry * @returns {object} */ - poll() { + poll(is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -105,7 +106,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.poll(true); + else return Promise.reject(error); }); } @@ -138,7 +150,7 @@ class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; @@ -183,7 +195,7 @@ class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; diff --git a/dist/adapter.js b/dist/adapter.js index 57c294f..a20309e 100644 --- a/dist/adapter.js +++ b/dist/adapter.js @@ -84,10 +84,11 @@ class Adapter { /** * Call the GET /receive endpoint and process the messages with decode * - * Returns the response + * Returns the response + * @param {boolean} is_retry * @returns {object} */ - poll() { + poll(is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -107,7 +108,18 @@ class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.poll(true); + else return Promise.reject(error); }); } @@ -140,7 +152,7 @@ class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; @@ -185,7 +197,7 @@ class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; diff --git a/src/adapter/index.js b/src/adapter/index.js index 51ecde7..3d48d0e 100644 --- a/src/adapter/index.js +++ b/src/adapter/index.js @@ -82,10 +82,11 @@ export class Adapter { /** * Call the GET /receive endpoint and process the messages with decode * - * Returns the response + * Returns the response + * @param {boolean} is_retry * @returns {object} */ - poll() { + poll(is_retry=false) { let adapterConfig = this.config; return this.getAuthorizationHeader() .then((headers) => { @@ -105,7 +106,18 @@ export class Adapter { return response; }) .catch((error) => { - return Promise.reject(error); + let retry = false; + switch(error.response.status) { + case 403: { + this.credentials = null; + retry = true; + } break; + case 503: { + retry = true; + } + } + if (retry && !is_retry) return this.poll(true); + else return Promise.reject(error); }); } @@ -138,7 +150,7 @@ export class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; @@ -183,7 +195,7 @@ export class Adapter { }) .catch((error) => { let retry = false; - switch(error.response.status_code) { + switch(error.response.status) { case 403: { this.credentials = null; retry = true; -- GitLab From 26a724dc3348c194e07bc53c4b277b7d73e0b0ae Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Mon, 20 Feb 2023 15:36:45 +0000 Subject: [PATCH 3/6] test: retry behaviour for 403s --- package.json | 2 +- test/cucumber/adapter/before.steps.js | 46 +++++++++++++++++++++--- test/cucumber/adapter/broadcast.steps.js | 23 +++++++++++- test/cucumber/adapter/common.steps.js | 10 ++++-- test/cucumber/adapter/poll.steps.js | 16 +++++++++ test/cucumber/adapter/publish.steps.js | 21 +++++++++++ yarn.lock | 4 +-- 7 files changed, 110 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 983a817..f8b9ee6 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "axios": "^1.2.3", "axios-mock-adapter": "^1.21.2", "babel-jest": "^27.4.4", - "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#2a22ae98", + "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691a", "cross-env": "^7.0.3", "eslint": "^8.4.1", "eslint-config-prettier": "^8.3.0", diff --git a/test/cucumber/adapter/before.steps.js b/test/cucumber/adapter/before.steps.js index d07405d..fc17ad9 100644 --- a/test/cucumber/adapter/before.steps.js +++ b/test/cucumber/adapter/before.steps.js @@ -14,6 +14,7 @@ const mockInvalidConfig = fixtures.get('config-invalid'); const mockSchema = fixtures.get('schema-swagger'); const { GenericProtocol } = require('../../../dist/protocol'); +const { Adapter } = require('../../../dist/adapter'); /** * Use assert.CallTracker to track internal method calls @@ -27,11 +28,41 @@ const trackedFunction = function(method, params) { }; const recorder = tracker.calls(trackedFunction); +class TrackedAdapter extends Adapter { + setupCallTracking(recorder, tracker) { + this.recorder = recorder; + this.tracker = tracker; + } + getAuthorizationHeader() { + this.recorder('getAuthorizationHeader'); + return super.getAuthorizationHeader(); + } + poll(is_retry) { + this.recorder('poll', { is_retry }); + return super.poll(is_retry); + } + publish(topic, body, is_retry) { + this.recorder('publish', { topic, body, is_retry }); + return super.publish(topic, body, is_retry); + } + broadcast(body, is_retry) { + this.recorder('broadcast', { body, is_retry }); + return super.broadcast(body, is_retry); + } + getTrackedCalls(method) { + let calls = this.tracker.getCalls(this.recorder); + let methodCalls = calls.filter(call => call.arguments[0] === method); + return methodCalls; + } + resetTracker() { + this.tracker.reset(); + } +} + class TrackedGenericProtocol extends GenericProtocol { - constructor(schema, services) { - super(schema, services); - this.recorder = services.recorder; - this.tracker = services.tracker; + setupCallTracking(recorder, tracker) { + this.recorder = recorder; + this.tracker = tracker; } encode(type, message) { this.recorder('encode', {type, message}); @@ -63,7 +94,12 @@ Before(function() { recorder, tracker }; - this.protocol = new TrackedGenericProtocol(this.schema, services); + this.classes = { + TrackedAdapter, + TrackedGenericProtocol, + }; + this.protocol = new this.classes.TrackedGenericProtocol(this.schema, services); + this.protocol.setupCallTracking(this.recorder, this.tracker); this.protocol.resetTracker(); this.mockAxios = mockAxios; this.mockAxios.reset(); diff --git a/test/cucumber/adapter/broadcast.steps.js b/test/cucumber/adapter/broadcast.steps.js index ad08de5..d0206a7 100644 --- a/test/cucumber/adapter/broadcast.steps.js +++ b/test/cucumber/adapter/broadcast.steps.js @@ -1,4 +1,5 @@ -const { When } = require('@cucumber/cucumber'); +const assert = require('assert'); +const { When, Then } = require('@cucumber/cucumber'); const { fixtures } = require('../../fixtures/server'); @@ -22,4 +23,24 @@ When('the broadcast method is called', function() { this.message = message; const body = JSON.stringify(message); this.call = this.adapter.broadcast(body); + this.broadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; +}); + +When('the broadcast method is called with is_retry on', function() { + const message = fixtures.get('message-vehicle-status'); + this.message = message; + const body = JSON.stringify(message); + this.call = this.adapter.broadcast(body, true); + this.broadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; +}); + +Then('the broadcast method was called with is_retry on', function() { + let broadcastCalls = this.adapter.getTrackedCalls('broadcast'); + let lastCall = broadcastCalls[broadcastCalls.length-1]; + assert.ok(lastCall.arguments[1].is_retry); +}); + +Then('the broadcast method is not called again', function() { + let newBroadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; + assert.equal(this.broadcastCallCount, newBroadcastCallCount); }); \ No newline at end of file diff --git a/test/cucumber/adapter/common.steps.js b/test/cucumber/adapter/common.steps.js index 3979c10..c48982e 100644 --- a/test/cucumber/adapter/common.steps.js +++ b/test/cucumber/adapter/common.steps.js @@ -6,7 +6,7 @@ const { fixtures } = require('../../fixtures/server'); const mockValidConfig = fixtures.get('config-valid'); const mockInvalidConfig = fixtures.get('config-invalid'); const mockSchema = require('../../mock/swagger.json'); -const { Adapter } = require('../../../dist/adapter'); +// const { Adapter } = require('../../../dist/adapter'); Given('valid config', function() { this.config = mockValidConfig @@ -18,8 +18,8 @@ Given('invalid config', function() { }); When('the adapter instance is created', function() { - let mockAdapter = new Adapter(this.protocol, this.config); - this.adapter = mockAdapter; + this.adapter = new this.classes.TrackedAdapter(this.protocol, this.config); + this.adapter.setupCallTracking(this.recorder, this.tracker); }); Then('a successful response is returned with status {int}', function(expectedStatus) { @@ -34,4 +34,8 @@ Then('an error response is returned with status {int}', function(expectedStatus) .catch((error) => { assert.equal(error.response.status, expectedStatus); }); +}); + +Then('the credentials are deleted', function() { + assert.equal(this.adapter.credentials, null); }); \ No newline at end of file diff --git a/test/cucumber/adapter/poll.steps.js b/test/cucumber/adapter/poll.steps.js index db3b564..60fc2c0 100644 --- a/test/cucumber/adapter/poll.steps.js +++ b/test/cucumber/adapter/poll.steps.js @@ -45,3 +45,19 @@ Then('the protocol {string} method is called {int} times', function(method, xInv const decodes = this.protocol.getTrackedCalls(method); assert.equal(decodes.length, xInvokes); }); + +When('the poll method is called with is_retry on', function() { + this.call = this.adapter.poll(true); + this.pollCallCount = this.adapter.getTrackedCalls('poll').length; +}); + +Then('the poll method was called with is_retry on', function() { + let pollCalls = this.adapter.getTrackedCalls('poll'); + let lastCall = pollCalls[pollCalls.length-1]; + assert.ok(lastCall.arguments[1].is_retry); +}); + +Then('the poll method is not called again', function() { + let newPollCallCount = this.adapter.getTrackedCalls('poll').length; + assert.equal(this.pollCallCount, newPollCallCount); +}); diff --git a/test/cucumber/adapter/publish.steps.js b/test/cucumber/adapter/publish.steps.js index 95a0f62..43648c0 100644 --- a/test/cucumber/adapter/publish.steps.js +++ b/test/cucumber/adapter/publish.steps.js @@ -24,4 +24,25 @@ When('the publish method is called', function() { const topic = message.metadata.destination; const body = JSON.stringify(message); this.call = this.adapter.publish(topic, body); + this.publishCallCount = this.adapter.getTrackedCalls('publish').length; +}); + +When('the publish method is called with is_retry on', function() { + const message = fixtures.get('message-vehicle-status'); + this.message = message; + const topic = message.metadata.destination; + const body = JSON.stringify(message); + this.call = this.adapter.publish(topic, body, true); + this.publishCallCount = this.adapter.getTrackedCalls('publish').length; +}); + +Then('the publish method was called with is_retry on', function() { + let publishCalls = this.adapter.getTrackedCalls('publish'); + let lastCall = publishCalls[publishCalls.length-1]; + assert.ok(lastCall.arguments[1].is_retry); +}); + +Then('the publish method is not called again', function() { + let newPublishCallCount = this.adapter.getTrackedCalls('publish').length; + assert.equal(this.publishCallCount, newPublishCallCount); }); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9f04020..15f970c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1318,9 +1318,9 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#2a22ae98": +"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691a": version "0.0.1" - resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#2a22ae98e9020a04895f79f6af5fb1c979f62259" + resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691affa53b04ac65b5fe7dc90df8e0286867" balanced-match@^1.0.0: version "1.0.2" -- GitLab From 1245857604c996aedf32b96251d1dd33eb930610 Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Tue, 21 Feb 2023 09:27:47 +0000 Subject: [PATCH 4/6] refactor: count api calls to /token on retries --- package.json | 2 +- test/cucumber/adapter/before.steps.js | 3 ++ test/cucumber/adapter/broadcast.steps.js | 14 ++++++---- test/cucumber/adapter/common.steps.js | 28 ++++++++++++++++++- .../adapter/get-authorization-header.steps.js | 6 ++-- test/cucumber/adapter/poll.steps.js | 13 ++++++--- test/cucumber/adapter/publish.steps.js | 14 ++++++---- yarn.lock | 4 +-- 8 files changed, 64 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index f8b9ee6..46409e3 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "axios": "^1.2.3", "axios-mock-adapter": "^1.21.2", "babel-jest": "^27.4.4", - "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691a", + "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a", "cross-env": "^7.0.3", "eslint": "^8.4.1", "eslint-config-prettier": "^8.3.0", diff --git a/test/cucumber/adapter/before.steps.js b/test/cucumber/adapter/before.steps.js index fc17ad9..1ac9d79 100644 --- a/test/cucumber/adapter/before.steps.js +++ b/test/cucumber/adapter/before.steps.js @@ -87,6 +87,7 @@ class TrackedGenericProtocol extends GenericProtocol { } Before(function() { + this.api = mockValidConfig.api; this.schema = mockSchema; this.tracker = tracker; this.recorder = recorder; @@ -98,11 +99,13 @@ Before(function() { TrackedAdapter, TrackedGenericProtocol, }; + this.callCounts = {}; this.protocol = new this.classes.TrackedGenericProtocol(this.schema, services); this.protocol.setupCallTracking(this.recorder, this.tracker); this.protocol.resetTracker(); this.mockAxios = mockAxios; this.mockAxios.reset(); + this.mockAxios.resetHistory(); this.mockAxios.onGet( `${mockValidConfig.api}/token`, diff --git a/test/cucumber/adapter/broadcast.steps.js b/test/cucumber/adapter/broadcast.steps.js index d0206a7..05a9cd0 100644 --- a/test/cucumber/adapter/broadcast.steps.js +++ b/test/cucumber/adapter/broadcast.steps.js @@ -12,10 +12,14 @@ When('a mock notify API response is configured to return success', function() { ).reply(200, response); }); -When('a mock notify API response is configured to return an error', function() { +When('a mock notify API response is configured to return a {int} error', function(statusCode) { + const statusMessages = { + 403: 'Token expired', + 503: 'Service unavailable' + }; this.mockAxios.onPost( `${mockValidConfig.api}/notify`, - ).reply(403, { message: 'Token expired' }) + ).reply(statusCode, { message: statusMessages[statusCode] }) }); When('the broadcast method is called', function() { @@ -23,7 +27,7 @@ When('the broadcast method is called', function() { this.message = message; const body = JSON.stringify(message); this.call = this.adapter.broadcast(body); - this.broadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; + this.callCounts.broadcast = this.adapter.getTrackedCalls('broadcast').length; }); When('the broadcast method is called with is_retry on', function() { @@ -31,7 +35,7 @@ When('the broadcast method is called with is_retry on', function() { this.message = message; const body = JSON.stringify(message); this.call = this.adapter.broadcast(body, true); - this.broadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; + this.callCounts.broadcast = this.adapter.getTrackedCalls('broadcast').length; }); Then('the broadcast method was called with is_retry on', function() { @@ -42,5 +46,5 @@ Then('the broadcast method was called with is_retry on', function() { Then('the broadcast method is not called again', function() { let newBroadcastCallCount = this.adapter.getTrackedCalls('broadcast').length; - assert.equal(this.broadcastCallCount, newBroadcastCallCount); + assert.equal(this.callCounts.broadcast, newBroadcastCallCount); }); \ No newline at end of file diff --git a/test/cucumber/adapter/common.steps.js b/test/cucumber/adapter/common.steps.js index c48982e..a0f065c 100644 --- a/test/cucumber/adapter/common.steps.js +++ b/test/cucumber/adapter/common.steps.js @@ -38,4 +38,30 @@ Then('an error response is returned with status {int}', function(expectedStatus) Then('the credentials are deleted', function() { assert.equal(this.adapter.credentials, null); -}); \ No newline at end of file +}); + +Then('the credentials are not deleted', function() { + assert.notEqual(this.adapter.credentials, null); +}); + +When('the {string} method call counts are checked', function(method) { + let newCallCount = this.adapter.getTrackedCalls(method).length; + this.callCounts[method] = newCallCount; +}); + +Then('the {string} method is not called again', function(method) { + let newCallCount = this.adapter.getTrackedCalls(method).length; + assert.equal(this.callCounts[method], newCallCount); +}); + +Then('the total number of calls to {string} was {int}', function(method, expectedCallCount) { + let callCount = this.adapter.getTrackedCalls(method).length; + assert.equal(callCount, expectedCallCount); +}); + +Then('the total number of {string} requests to {string} was {int}', function(method, endpoint, expectedCallCount) { + let url = `${this.api}${endpoint}`; + let requestHistory = this.mockAxios.history[method.toLowerCase()].filter((request) => request.url === url); + assert.equal(requestHistory.length, expectedCallCount); +}); + diff --git a/test/cucumber/adapter/get-authorization-header.steps.js b/test/cucumber/adapter/get-authorization-header.steps.js index af31d0b..a9fd652 100644 --- a/test/cucumber/adapter/get-authorization-header.steps.js +++ b/test/cucumber/adapter/get-authorization-header.steps.js @@ -2,7 +2,9 @@ const assert = require('assert'); const { When, Then } = require('@cucumber/cucumber'); When('the getAuthorizationHeader method is called', function() { - this.call = this.adapter.getAuthorizationHeader() + this.call = this.adapter.getAuthorizationHeader(); + let callCount = this.adapter.getTrackedCalls('getAuthorizationHeader').length; + this.callCounts.getAuthorizationHeader = callCount; }); Then('a headers object is returned containing a bearer token authorization header', function() { @@ -12,4 +14,4 @@ Then('a headers object is returned containing a bearer token authorization heade assert.ok(authHeaderWords[0] === 'Bearer'); assert.ok(authHeaderWords[1] === this.adapter.credentials.token); }); -}); \ No newline at end of file +}); diff --git a/test/cucumber/adapter/poll.steps.js b/test/cucumber/adapter/poll.steps.js index 60fc2c0..a2b50a0 100644 --- a/test/cucumber/adapter/poll.steps.js +++ b/test/cucumber/adapter/poll.steps.js @@ -24,14 +24,19 @@ When('a mock receive API response is configured to return {int} messages', funct ).reply(200, response); }); -When('a mock receive API response is configured to return an error', function() { +When('a mock receive API response is configured to return a {int} error', function(statusCode) { + const statusMessages = { + 403: 'Token expired', + 503: 'Service unavailable' + }; this.mockAxios.onGet( `${mockValidConfig.api}/receive`, - ).reply(403, { message: 'Token expired' }) + ).reply(statusCode, { message: statusMessages[statusCode] }) }); When('the poll method is called', function() { this.call = this.adapter.poll(); + this.callCounts.poll = this.adapter.getTrackedCalls('poll').length; }); Then('a successful response is returned with {int} messages', function(xMessages) { @@ -48,7 +53,7 @@ Then('the protocol {string} method is called {int} times', function(method, xInv When('the poll method is called with is_retry on', function() { this.call = this.adapter.poll(true); - this.pollCallCount = this.adapter.getTrackedCalls('poll').length; + this.callCounts.poll = this.adapter.getTrackedCalls('poll').length; }); Then('the poll method was called with is_retry on', function() { @@ -59,5 +64,5 @@ Then('the poll method was called with is_retry on', function() { Then('the poll method is not called again', function() { let newPollCallCount = this.adapter.getTrackedCalls('poll').length; - assert.equal(this.pollCallCount, newPollCallCount); + assert.equal(this.callCounts.poll, newPollCallCount); }); diff --git a/test/cucumber/adapter/publish.steps.js b/test/cucumber/adapter/publish.steps.js index 43648c0..e207488 100644 --- a/test/cucumber/adapter/publish.steps.js +++ b/test/cucumber/adapter/publish.steps.js @@ -12,10 +12,14 @@ When('a mock send API response is configured to return success', function() { ).reply(200, response); }); -When('a mock send API response is configured to return an error', function() { +When('a mock send API response is configured to return a {int} error', function(statusCode) { + const statusMessages = { + 403: 'Token expired', + 503: 'Service unavailable' + }; this.mockAxios.onPost( `${mockValidConfig.api}/send`, - ).reply(403, { message: 'Token expired' }) + ).reply(statusCode, { message: statusMessages[statusCode] }) }); When('the publish method is called', function() { @@ -24,7 +28,7 @@ When('the publish method is called', function() { const topic = message.metadata.destination; const body = JSON.stringify(message); this.call = this.adapter.publish(topic, body); - this.publishCallCount = this.adapter.getTrackedCalls('publish').length; + this.callCounts.publish = this.adapter.getTrackedCalls('publish').length; }); When('the publish method is called with is_retry on', function() { @@ -33,7 +37,7 @@ When('the publish method is called with is_retry on', function() { const topic = message.metadata.destination; const body = JSON.stringify(message); this.call = this.adapter.publish(topic, body, true); - this.publishCallCount = this.adapter.getTrackedCalls('publish').length; + this.callCounts.publish = this.adapter.getTrackedCalls('publish').length; }); Then('the publish method was called with is_retry on', function() { @@ -44,5 +48,5 @@ Then('the publish method was called with is_retry on', function() { Then('the publish method is not called again', function() { let newPublishCallCount = this.adapter.getTrackedCalls('publish').length; - assert.equal(this.publishCallCount, newPublishCallCount); + assert.equal(this.callCounts.publish, newPublishCallCount); }); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 15f970c..38992e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1318,9 +1318,9 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691a": +"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a": version "0.0.1" - resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dd29691affa53b04ac65b5fe7dc90df8e0286867" + resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a217eda84f05c91a9a7181d5cfad0aace" balanced-match@^1.0.0: version "1.0.2" -- GitLab From 9eee6fcd5b619cf99d5490c040095baacb7efe65 Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Tue, 21 Feb 2023 17:12:38 +0000 Subject: [PATCH 5/6] fix: update test suite commit ref Switch to fake domain for adapter config --- package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 46409e3..5e7a732 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "axios": "^1.2.3", "axios-mock-adapter": "^1.21.2", "babel-jest": "^27.4.4", - "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a", + "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c59", "cross-env": "^7.0.3", "eslint": "^8.4.1", "eslint-config-prettier": "^8.3.0", diff --git a/yarn.lock b/yarn.lock index 38992e1..a7a8c96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1318,9 +1318,9 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a": +"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c59": version "0.0.1" - resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#1fd8622a217eda84f05c91a9a7181d5cfad0aace" + resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c5981b898548841992b6f56a091c54ab5af" balanced-match@^1.0.0: version "1.0.2" -- GitLab From b2a02056e8e4821d7e8cf0c9447f2c7d8a381e84 Mon Sep 17 00:00:00 2001 From: Dan Jones <dan.jones@noc.ac.uk> Date: Wed, 22 Feb 2023 12:58:26 +0000 Subject: [PATCH 6/6] test: revert testsuite to install #dev --- package.json | 2 +- yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5e7a732..bc3b97b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "axios": "^1.2.3", "axios-mock-adapter": "^1.21.2", "babel-jest": "^27.4.4", - "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c59", + "backbone-adapter-testsuite": "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dev", "cross-env": "^7.0.3", "eslint": "^8.4.1", "eslint-config-prettier": "^8.3.0", diff --git a/yarn.lock b/yarn.lock index a7a8c96..38ff398 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1318,9 +1318,9 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c59": +"backbone-adapter-testsuite@git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#dev": version "0.0.1" - resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#09029c5981b898548841992b6f56a091c54ab5af" + resolved "git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#3a5b94e16188f53464ea06b9761e67292f5c07ff" balanced-match@^1.0.0: version "1.0.2" -- GitLab