Unverified Commit b3e0ef86 authored by Dan Jones's avatar Dan Jones
Browse files

test: add poll and publish tests

+ refactor cucumber step functions into test folder
+ rename features after tested adapter methods
+ refactor adapter.validate to call adapter.protocol.validate
parent 1ff6f48a
module.exports = { module.exports = {
default: `--format-options '{"snippetInterface": "synchronous"}'` default: {
} formatOptions: {
\ No newline at end of file snippetInterface: "synchronous"
},
paths: [ 'features/**/*.feature' ],
require: [ 'test/cucumber/**/*.steps.js' ],
},
};
import Validator from 'swagger-model-validator';
import axios from 'axios'; import axios from 'axios';
/** /**
...@@ -9,7 +8,6 @@ class Adapter { ...@@ -9,7 +8,6 @@ class Adapter {
this.protocol = protocol; this.protocol = protocol;
this.config = config; this.config = config;
this.axios = axios; this.axios = axios;
this.validator = new Validator(protocol.schema);
} }
/** /**
...@@ -25,13 +23,7 @@ class Adapter { ...@@ -25,13 +23,7 @@ class Adapter {
* @returns {object} * @returns {object}
*/ */
validate(message) { validate(message) {
return this.validator.validate( return this.protocol.validate(message);
message,
this.protocol.schema.components.schemas.Message,
this.protocol.schema.components.schemas,
false,
true
);
} }
/** /**
...@@ -113,8 +105,7 @@ class Adapter { ...@@ -113,8 +105,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
console.error(error); return Promise.reject(error);
return Promise.reject(error.response);
}); });
} }
...@@ -145,7 +136,7 @@ class Adapter { ...@@ -145,7 +136,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
...@@ -178,7 +169,7 @@ class Adapter { ...@@ -178,7 +169,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
} }
......
'use strict'; 'use strict';
var Validator = require('swagger-model-validator');
var axios = require('axios'); var axios = require('axios');
/** /**
...@@ -11,7 +10,6 @@ class Adapter { ...@@ -11,7 +10,6 @@ class Adapter {
this.protocol = protocol; this.protocol = protocol;
this.config = config; this.config = config;
this.axios = axios; this.axios = axios;
this.validator = new Validator(protocol.schema);
} }
/** /**
...@@ -27,13 +25,7 @@ class Adapter { ...@@ -27,13 +25,7 @@ class Adapter {
* @returns {object} * @returns {object}
*/ */
validate(message) { validate(message) {
return this.validator.validate( return this.protocol.validate(message);
message,
this.protocol.schema.components.schemas.Message,
this.protocol.schema.components.schemas,
false,
true
);
} }
/** /**
...@@ -115,8 +107,7 @@ class Adapter { ...@@ -115,8 +107,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
console.error(error); return Promise.reject(error);
return Promise.reject(error.response);
}); });
} }
...@@ -147,7 +138,7 @@ class Adapter { ...@@ -147,7 +138,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
...@@ -180,7 +171,7 @@ class Adapter { ...@@ -180,7 +171,7 @@ class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
} }
......
...@@ -37,8 +37,8 @@ class GenericProtocol { ...@@ -37,8 +37,8 @@ class GenericProtocol {
validate(message) { validate(message) {
return this.validator.validate( return this.validator.validate(
message, message,
this.protocol.schema.components.schemas.Message, this.schema.components.schemas.Message,
this.protocol.schema.components.schemas, this.schema.components.schemas,
false, false,
true true
); );
......
...@@ -39,8 +39,8 @@ class GenericProtocol { ...@@ -39,8 +39,8 @@ class GenericProtocol {
validate(message) { validate(message) {
return this.validator.validate( return this.validator.validate(
message, message,
this.protocol.schema.components.schemas.Message, this.schema.components.schemas.Message,
this.protocol.schema.components.schemas, this.schema.components.schemas,
false, false,
true true
); );
......
...@@ -9,7 +9,7 @@ Feature: Can the adapter receive messages? ...@@ -9,7 +9,7 @@ Feature: Can the adapter receive messages?
Given valid config Given valid config
When the adapter instance is created When the adapter instance is created
When the auth method is called When the auth method is called
When the queue contains 0 messages When a mock receive API response is configured to return 0 messages
When the poll method is called When the poll method is called
Then a successful response is returned with 0 messages Then a successful response is returned with 0 messages
...@@ -17,14 +17,26 @@ Feature: Can the adapter receive messages? ...@@ -17,14 +17,26 @@ Feature: Can the adapter receive messages?
Given valid config Given valid config
When the adapter instance is created When the adapter instance is created
When the auth method is called When the auth method is called
When the queue contains 2 messages When a mock receive API response is configured to return 2 messages
When the poll method is called When the poll method is called
Then a successful response is returned with 2 messages Then a successful response is returned with 2 messages
Then the protocol "validate" method is called 2 times
Then the protocol "decode" method is called 2 times
Scenario: 10 messages are received succecssfully if the queue contains 10 messages Scenario: 10 messages are received succecssfully if the queue contains 10 messages
Given valid config Given valid config
When the adapter instance is created When the adapter instance is created
When the auth method is called When the auth method is called
When the queue contains 10 messages When a mock receive API response is configured to return 10 messages
When the poll method is called When the poll method is called
Then a successful response is returned with 10 messages Then a successful response is returned with 10 messages
Then the protocol "validate" method is called 10 times
Then the protocol "decode" method is called 10 times
Scenario: An invalid token returns a forbidden response
Given valid config
When the adapter instance is created
When the auth method is called
When a mock receive API response is configured to return an error
When the poll method is called
Then an error response is returned with status 403
Feature: Can the adapter publish messages?
The adapter publish method works as expected
Scenario: A message can be published successfully
Given valid config
When the adapter instance is created
When the auth method is called
When a mock send API response is configured to return success
When the publish method is called
Then a successful response is returned with status 200
Scenario: A failed publish returns a 403
Given valid config
When the adapter instance is created
When the auth method is called
When a mock send API response is configured to return an error
When the publish method is called
Then an error response is returned with status 403
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
"lint": "yarn lint:js && yarn lint:prettier", "lint": "yarn lint:js && yarn lint:prettier",
"lintfix": "prettier --write --list-different . && yarn lint:js --fix", "lintfix": "prettier --write --list-different . && yarn lint:js --fix",
"prepare": "husky install", "prepare": "husky install",
"test": "yarn build && yarn cucumber-js", "test": "yarn build && yarn cucumber",
"cucumber": "cucumber-js",
"build": "cross-env NODE_ENV=production rollup -c" "build": "cross-env NODE_ENV=production rollup -c"
}, },
"lint-staged": { "lint-staged": {
......
import Validator from 'swagger-model-validator';
import axios from 'axios'; import axios from 'axios';
/** /**
...@@ -9,7 +8,6 @@ export class Adapter { ...@@ -9,7 +8,6 @@ export class Adapter {
this.protocol = protocol; this.protocol = protocol;
this.config = config; this.config = config;
this.axios = axios; this.axios = axios;
this.validator = new Validator(protocol.schema);
} }
/** /**
...@@ -25,13 +23,7 @@ export class Adapter { ...@@ -25,13 +23,7 @@ export class Adapter {
* @returns {object} * @returns {object}
*/ */
validate(message) { validate(message) {
return this.validator.validate( return this.protocol.validate(message);
message,
this.protocol.schema.components.schemas.Message,
this.protocol.schema.components.schemas,
false,
true
);
} }
/** /**
...@@ -113,8 +105,7 @@ export class Adapter { ...@@ -113,8 +105,7 @@ export class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
console.error(error); return Promise.reject(error);
return Promise.reject(error.response);
}); });
} }
...@@ -145,7 +136,7 @@ export class Adapter { ...@@ -145,7 +136,7 @@ export class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
...@@ -178,7 +169,7 @@ export class Adapter { ...@@ -178,7 +169,7 @@ export class Adapter {
return response; return response;
}) })
.catch((error) => { .catch((error) => {
return Promise.reject(error.response); return Promise.reject(error);
}); });
} }
} }
...@@ -37,8 +37,8 @@ export class GenericProtocol { ...@@ -37,8 +37,8 @@ export class GenericProtocol {
validate(message) { validate(message) {
return this.validator.validate( return this.validator.validate(
message, message,
this.protocol.schema.components.schemas.Message, this.schema.components.schemas.Message,
this.protocol.schema.components.schemas, this.schema.components.schemas,
false, false,
true true
); );
......
const assert = require('assert');
const { Given, When, Then } = require('@cucumber/cucumber');
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');
Given('valid config', function() {
this.config = mockValidConfig
});
When('the adapter instance is created', function() {
//let mockProtocol = new GenericProtocol(this.schema);
let mockAdapter = new Adapter(this.protocol, this.config);
this.adapter = mockAdapter;
});
When('the auth method is called', async function() {
await this.adapter.auth();
});
Then('the adapter credentials are populated', function() {
assert.equal(this.adapter.credentials.token, fixtures.get('response-valid-token').token);
});
Given('invalid config', function() {
this.schema = mockSchema;
this.config = mockInvalidConfig;
});
Then('the adapter auth fails', function() {
this.adapter.auth()
.catch((error) => {
assert.equal(error.response.status, 403);
});
});
\ No newline at end of file
const assert = require('assert'); const assert = require('assert');
const { Before, Given, When, Then } = require('@cucumber/cucumber'); const { Before } = require('@cucumber/cucumber');
const axios = require("axios"); const axios = require("axios");
const MockAdapter = require("axios-mock-adapter"); const MockAdapter = require("axios-mock-adapter");
...@@ -7,24 +7,64 @@ const MockAdapter = require("axios-mock-adapter"); ...@@ -7,24 +7,64 @@ const MockAdapter = require("axios-mock-adapter");
// This sets the mock adapter on the default instance // This sets the mock adapter on the default instance
const mockAxios = new MockAdapter(axios); const mockAxios = new MockAdapter(axios);
const { fixtures } = require('../../test/fixtures/server'); const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid'); const mockValidConfig = fixtures.get('config-valid');
const mockInvalidConfig = fixtures.get('config-invalid'); const mockInvalidConfig = fixtures.get('config-invalid');
const mockSchema = require('../../test/mock/swagger.json'); const mockSchema = require('../../mock/swagger.json');
const { Adapter } = require('../../dist/adapter'); const { GenericProtocol } = require('../../../dist/protocol');
const { GenericProtocol } = require('../../dist/protocol');
/**
* Use assert.CallTracker to track internal method calls
* Instead of adding trackers to each method, create a
* single tracked stub function and then use the parameters
* to record what is being tracked.
*/
const tracker = new assert.CallTracker();
const trackedFunction = function(method, params) {
// do nothing;
};
const recorder = tracker.calls(trackedFunction);
let decodeTracker;
class TrackedGenericProtocol extends GenericProtocol { class TrackedGenericProtocol extends GenericProtocol {
decode() { constructor(schema, services) {
return super.decode() super(schema, services);
this.recorder = services.recorder;
this.tracker = services.tracker;
}
encode(type, message) {
this.recorder('encode', {type, message});
return super.encode(type, message)
}
decode(type, message) {
this.recorder('decode', {type, message});
return super.decode(type, message)
}
validate(message) {
this.recorder('validate', {message});
return super.validate(message);
}
getTrackedCalls(method) {
let calls = this.tracker.getCalls(this.recorder);
let methodCalls = calls.filter(call => call.arguments[0] === method);
return methodCalls;
}
resetTracker() {
this.tracker.reset();
} }
} }
Before(function() { Before(function() {
this.schema = mockSchema;
this.tracker = tracker;
this.recorder = recorder;
let services = {
recorder: this.recorder,
tracker: this.tracker
};
this.protocol = new TrackedGenericProtocol(this.schema, services);
this.protocol.resetTracker();
this.mockAxios = mockAxios; this.mockAxios = mockAxios;
this.mockAxios.reset(); this.mockAxios.reset();
...@@ -38,35 +78,4 @@ Before(function() { ...@@ -38,35 +78,4 @@ Before(function() {
{ params: { client_id: mockInvalidConfig.client_id, secret: mockInvalidConfig.secret } } { params: { client_id: mockInvalidConfig.client_id, secret: mockInvalidConfig.secret } }
).reply(403, fixtures.get('response-denied-token')); ).reply(403, fixtures.get('response-denied-token'));
});
Given('valid config', function() {
this.schema = mockSchema;
this.config = mockValidConfig
});
When('the adapter instance is created', function() {
let mockProtocol = new GenericProtocol(this.schema);
let mockAdapter = new Adapter(mockProtocol, this.config);
this.adapter = mockAdapter;
});
When('the auth method is called', async function() {
await this.adapter.auth();
});
Then(('the adapter credentials are populated'), function() {
assert.equal(this.adapter.credentials.token, fixtures.get('response-valid-token').token);
});
Given('invalid config', function() {
this.schema = mockSchema;
this.config = mockInvalidConfig;
});
Then('the adapter auth fails', async function() {
this.adapter.auth()
.catch((error) => {
assert.equal(error.response.status, 403);
});
}); });
\ No newline at end of file
const assert = require('assert'); const assert = require('assert');
const { Before, Given, When, Then } = require('@cucumber/cucumber'); const { When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../test/fixtures/server'); const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid'); const mockValidConfig = fixtures.get('config-valid');
...@@ -17,20 +17,38 @@ const xMessageResponse = function(xMessages) { ...@@ -17,20 +17,38 @@ const xMessageResponse = function(xMessages) {
return response; return response;
}; };
When('the queue contains {int} messages', function(xMessages) { When('a mock receive API response is configured to return {int} messages', function(xMessages) {
const response = xMessageResponse(xMessages); const response = xMessageResponse(xMessages);
this.mockAxios.onGet( this.mockAxios.onGet(
`${mockValidConfig.api}/receive`, `${mockValidConfig.api}/receive`,
).reply(200, response); ).reply(200, response);
}); });
When('the poll method is called', async function() { When('a mock receive API response is configured to return an error', function() {
this.messages = await this.adapter.poll() this.mockAxios.onGet(
.then((response) => { `${mockValidConfig.api}/receive`,
return response.data; ).reply(403, { message: 'Token expired' })
}); });
When('the poll method is called', function() {
this.call = this.adapter.poll();
}); });
Then('a successful response is returned with {int} messages', function(xMessages) { Then('a successful response is returned with {int} messages', function(xMessages) {
assert.equal(this.messages.length, xMessages); this.call
}); .then(response => {
\ No newline at end of file assert.equal(response.data.length, xMessages);
});
});
Then('the protocol {string} method is called {int} times', function(method, xInvokes) {
const decodes = this.protocol.getTrackedCalls(method);
assert.equal(decodes.length, xInvokes);
});
Then('an error response is returned with status {int}', function(expectedStatus) {
this.call
.catch((error) => {
assert.equal(error.response.status, expectedStatus);
});
});
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid');
When('a mock send API response is configured to return success', function() {
const response = {};
this.mockAxios.onPost(
`${mockValidConfig.api}/send`,
).reply(200, response);
});
When('a mock send API response is configured to return an error', function() {
this.mockAxios.onPost(
`${mockValidConfig.api}/send`,
).reply(403, { message: 'Token expired' })
});
When('the publish method is called', 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);
});
Then('a successful response is returned with status {int}', function(expectedStatus) {
this.call
.then(response => {
assert.equal(response.status, expectedStatus);
});
});
\ No newline at end of file
const assert = require('assert'); const assert = require('assert');
const { Before, Given, When, Then } = require('@cucumber/cucumber'); const { Given, When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../test/fixtures/server'); const { fixtures } = require('../../fixtures/server');
Given('a valid message', function() { Given('a valid message', function() {
this.message = fixtures.get('message-vehicle-status'); this.message = fixtures.get('message-vehicle-status');
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment