Commit 03de0f57 authored by Dan Jones's avatar Dan Jones
Browse files

Merge branch '3-implement-bdd-tests-in-cucumber' into...

Merge branch '3-implement-bdd-tests-in-cucumber' into '1-import-prototype-adapter-code-from-example-web-client'

Resolve "Implement bdd tests in cucumber"

See merge request !2
parents ec074c62 645b90f6
......@@ -35,7 +35,13 @@ export class GenericProtocol {
* @returns {object}
*/
validate(message) {
return this.validator.validate(message, this.schema.definitions.Message);
return this.validator.validate(
message,
this.schema.components.schemas.Message,
this.schema.components.schemas,
false,
true
);
}
/**
......@@ -44,7 +50,11 @@ export class GenericProtocol {
* @returns {string}
*/
getType(message) {
return message.message_type;
try {
return message.payload.message_type;
} catch(error) {
return null;
}
}
/**
......
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../fixtures/server');
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);
});
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 { Before } = require('@cucumber/cucumber');
const axios = require("axios");
const MockAdapter = require("axios-mock-adapter");
// This sets the mock adapter on the default instance
const mockAxios = new MockAdapter(axios);
const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid');
const mockInvalidConfig = fixtures.get('config-invalid');
const mockSchema = require('../../mock/swagger.json');
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);
class TrackedGenericProtocol extends GenericProtocol {
constructor(schema, services) {
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() {
this.schema = mockSchema;
this.tracker = tracker;
this.recorder = recorder;
let services = {
recorder,
tracker
};
this.protocol = new TrackedGenericProtocol(this.schema, services);
this.protocol.resetTracker();
this.mockAxios = mockAxios;
this.mockAxios.reset();
this.mockAxios.onGet(
`${mockValidConfig.api}/token`,
{ params: { client_id: mockValidConfig.client_id, secret: mockValidConfig.secret } }
).reply(200, fixtures.get('response-valid-token'));
this.mockAxios.onGet(
`${mockInvalidConfig.api}/token`,
{ params: { client_id: mockInvalidConfig.client_id, secret: mockInvalidConfig.secret } }
).reply(403, fixtures.get('response-denied-token'));
});
\ No newline at end of file
const { When } = require('@cucumber/cucumber');
const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid');
When('a mock notify API response is configured to return success', function() {
const response = {};
this.mockAxios.onPost(
`${mockValidConfig.api}/notify`,
).reply(200, response);
});
When('a mock notify API response is configured to return an error', function() {
this.mockAxios.onPost(
`${mockValidConfig.api}/notify`,
).reply(403, { message: 'Token expired' })
});
When('the broadcast method is called', function() {
const message = fixtures.get('message-vehicle-status');
this.message = message;
const body = JSON.stringify(message);
this.call = this.adapter.broadcast(body);
});
\ No newline at end of file
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
});
Given('invalid config', function() {
this.schema = mockSchema;
this.config = mockInvalidConfig;
});
When('the adapter instance is created', function() {
let mockAdapter = new Adapter(this.protocol, this.config);
this.adapter = mockAdapter;
});
Then('a successful response is returned with status {int}', function(expectedStatus) {
this.call
.then(response => {
assert.equal(response.status, expectedStatus);
});
});
Then('an error response is returned with status {int}', function(expectedStatus) {
this.call
.catch((error) => {
assert.equal(error.response.status, expectedStatus);
});
});
\ No newline at end of file
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
When('the getAuthorizationHeader method is called', function() {
this.call = this.adapter.getAuthorizationHeader()
});
Then('a headers object is returned containing a bearer token authorization header', function() {
this.call.then(headers => {
assert.ok('Authorization' in headers);
const authHeaderWords = headers.Authorization.split(" ");
assert.ok(authHeaderWords[0] === 'Bearer');
assert.ok(authHeaderWords[1] === this.adapter.credentials.token);
});
});
\ No newline at end of file
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../fixtures/server');
const mockValidConfig = fixtures.get('config-valid');
const xMessageResponse = function(xMessages) {
const message = fixtures.get('message-vehicle-status');
let response = [];
for (let i=0; i<xMessages; i++) {
response.push({
topic: "broadcast",
message: JSON.stringify(message)
});
}
return response;
};
When('a mock receive API response is configured to return {int} messages', function(xMessages) {
const response = xMessageResponse(xMessages);
this.mockAxios.onGet(
`${mockValidConfig.api}/receive`,
).reply(200, response);
});
When('a mock receive API response is configured to return an error', function() {
this.mockAxios.onGet(
`${mockValidConfig.api}/receive`,
).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) {
this.call
.then(response => {
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);
});
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);
});
\ No newline at end of file
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
When('the token expiry is in the future', function() {
const expiry = new Date();
expiry.setHours(expiry.getHours()+1);
this.adapter.credentials.expiry = expiry.toISOString();
});
When('the token expiry is in the past', function() {
const expiry = new Date();
expiry.setHours(expiry.getHours()-1);
this.adapter.credentials.expiry = expiry.toISOString();
});
// Boolean parameters are not supported
// and returns "true" would be misleading
Then('tokenValid returns true', function() {
const isValid = this.adapter.tokenValid();
assert.ok(isValid);
});
Then('tokenValid returns false', function() {
const isValid = this.adapter.tokenValid();
assert.ok(!isValid);
});
\ No newline at end of file
const assert = require('assert');
const { Given, When, Then } = require('@cucumber/cucumber');
const { fixtures } = require('../../fixtures/server');
Given('a valid message', function() {
this.message = fixtures.get('message-vehicle-status');
});
Given('an invalid message', function() {
this.message = fixtures.get('message-vehicle-status-invalid');
});
When('the validate method is called', function() {
this.validation = this.adapter.validate(this.message);
});
\ No newline at end of file
const assert = require('assert');
const { Then } = require('@cucumber/cucumber');
Then('the message is returned unaltered', function() {
assert.equal(this.message, this.response);
});
\ No newline at end of file
const { When } = require('@cucumber/cucumber');
When('the protocol.decode method is called', function() {
const type = this.protocol.getType(this.message);
this.response = this.protocol.decode(type, this.message);
});
\ No newline at end of file
const { When } = require('@cucumber/cucumber');
When('the protocol.encode method is called', function() {
const type = this.protocol.getType(this.message);
this.response = this.protocol.encode(type, this.message);
});
\ No newline at end of file
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
When('protocol getType is called', function() {
this.type = this.protocol.getType(this.message);
});
Then('getType returns message.payload.message_type if present', function() {
assert.equal(this.type, this.message.payload.message_type);
});
Then('getType returns null if message.payload.message_type is not present', function() {
assert.equal(this.type, null);
});
\ No newline at end of file
const assert = require('assert');
const { When, Then } = require('@cucumber/cucumber');
When('the protocol.validate method is called', function() {
this.validation = this.protocol.validate(this.message);
});
Then('the message is validated successfully', function() {
assert.equal(this.validation.valid, true);
assert.equal(this.validation.errorCount, 0);
});
Then('the message fails to validate', function() {
assert.equal(this.validation.valid, false);
assert.notEqual(this.validation.errorCount, 0);
});
\ No newline at end of file
const assert = require('assert');
const { Given, When, Then } = require('@cucumber/cucumber');
const OpenAPISchemaValidator = require('openapi-schema-validator').default;
const fs = require('fs');
const schemaLocation = './test/mock/swagger.json';
Given('the test schema', function() {
this.schema = JSON.parse(fs.readFileSync(schemaLocation));
});
When('it is validated', function() {
const validator = new OpenAPISchemaValidator({ version: 3 });
this.validation = validator.validate(this.schema);
});
Then('it matches the OpenAPI specification', function() {
// According to the docs this should return a valid:boolean
// but if you look at the code it just returns a list of errors
// which is empty for a valid result
assert.equal(this.validation.errors.length, 0);
});
\ No newline at end of file
{
"api": "https://example.backbone.com/api",
"client_id": "invalid-client-id",
"client_name": "InvalidClientName",
"subscription": "dot.delimited.topic.subscription.#",
"secret": "TheCollaredDoveCoosInTheChimneyPot"
}
\ No newline at end of file
{
"api": "https://example.backbone.com/api",
"client_id": "unique-client-id",
"client_name": "UniqueClientName",
"subscription": "dot.delimited.topic.subscription.#",
"secret": "TheGeeseFlySouthInWinter"
}
\ No newline at end of file
{
"metadata": {
"source": "ae",
"destination": "soar.po.ecosub.eco1",
"delivery_type": "publish",
"message_id": "test"
},
"payload": {
"messagetype": "VehicleStatus",
"operatorID": 1,
"vehicleID": 12,
"coordinates": {
"latitude": "monkeys",
"longitude": "janvier",
"depth": "twenty five metres please",
"projection": 4326
},
"battery_percentage": "plenty"
}
}
\ No newline at end of file
{
"metadata": {
"source": "ae",
"destination": "soar.po.ecosub.eco1",
"delivery_type": "publish",
"message_id": "test"
},
"payload": {
"message_type": "VehicleStatus",
"operator_id": "po",
"vehicle_id": "eco1",
"coordinates": {
"latitude": 57.234,
"longitude": -8.432,
"depth": 50,
"projection": 4326
},
"battery_percentage": 64
}
}
\ No newline at end of file
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