consumer-contracts
Advanced tools
Comparing version 1.5.0 to 3.0.1
#! /usr/bin/env node | ||
var program = require('commander'); | ||
var package = require('../package'); | ||
var runner = require('./runner'); | ||
const program = require('commander'); | ||
const package = require('../package'); | ||
const runner = require('./runner'); | ||
@@ -15,2 +15,2 @@ program | ||
if (!program.args.length) program.help(); | ||
if (!program.args.length) program.help(); |
@@ -1,9 +0,11 @@ | ||
var Joi = require('joi'); | ||
var util = require('util'); | ||
var debug = require('debug')('consumer-contracts'); | ||
var _ = require('lodash'); | ||
var async = require('async'); | ||
var package = require('../package'); | ||
'use strict'; | ||
var requiredOptions = [ | ||
const _ = require('lodash'); | ||
const async = require('async'); | ||
const debug = require('debug')('consumer-contracts'); | ||
const Joi = require('joi'); | ||
const pkg = require('../package'); | ||
const requiredOptions = [ | ||
'name', | ||
@@ -15,3 +17,3 @@ 'consumer', | ||
var joiOptions = { | ||
const joiOptions = { | ||
allowUnknown: true, | ||
@@ -23,5 +25,5 @@ presence: 'required' | ||
options = options || {}; | ||
requiredOptions.forEach(function (key) { | ||
requiredOptions.forEach((key) => { | ||
if (!options.hasOwnProperty(key)) { | ||
throw new Error('Invalid contract: Missing required property [' + key + ']'); | ||
throw new Error(`Invalid contract: Missing required property [${key}]`); | ||
} | ||
@@ -31,59 +33,64 @@ }); | ||
function Contract(options) { | ||
validateOptions(options); | ||
this.name = options.name; | ||
this.consumer = options.consumer; | ||
this.request = options.request; | ||
this.response = options.response; | ||
this.client = (options.client || require('request')).defaults({ | ||
json: true, | ||
headers: { | ||
'user-agent': 'consumer-contracts/' + package.version | ||
} | ||
}); | ||
this.before = options.before; | ||
this.after = options.after; | ||
if (options.joiOptions) { | ||
this.joiOptions = joiOptions = _.merge(joiOptions, options.joiOptions); | ||
} | ||
function createError(detail, path, res) { | ||
const err = new Error(`Contract failed: ${detail.message}`); | ||
err.detail = `at res.${path} got [${_.get(res, path)}]`; | ||
return err; | ||
} | ||
Contract.prototype.validate = function (cb) { | ||
var schema = Joi.object().keys(this.response); | ||
var client = this.client; | ||
var request = this.request; | ||
var tasks = []; | ||
class Contract { | ||
constructor(options) { | ||
validateOptions(options); | ||
this.name = options.name; | ||
this.consumer = options.consumer; | ||
this.before = options.before; | ||
this.after = options.after; | ||
if (this.before) { | ||
tasks.push(this.before); | ||
this._request = options.request; | ||
this._response = options.response; | ||
this._client = (options.client || require('request')).defaults({ | ||
json: true, | ||
headers: { | ||
'user-agent': 'consumer-contracts/' + pkg.version | ||
} | ||
}); | ||
if (options.joiOptions) { | ||
this.joiOptions = _.merge(joiOptions, options.joiOptions); | ||
} | ||
} | ||
tasks.push(function (done) { | ||
client(request, function (err, res) { | ||
if (err) return done(new Error(util.format('Request failed: %s', err.message))); | ||
validate(cb) { | ||
const schema = Joi.object().keys(this._response); | ||
const tasks = []; | ||
debug(util.format('%s %s %s', res.request.method, res.request.href, res.statusCode)); | ||
if (this.before) { | ||
tasks.push(this.before); | ||
} | ||
var result = Joi.validate(res, schema, joiOptions); | ||
var path; | ||
var detail; | ||
tasks.push((done) => { | ||
this._client(this._request, (err, res) => { | ||
if (err) return done(new Error(`Request failed: ${err.message}`)); | ||
if (result.error) { | ||
detail = result.error.details[0]; | ||
path = detail.path; | ||
err = new Error(util.format('Contract failed: %s', detail.message)); | ||
err.detail = util.format('at res.%s got [%s]', path, _.get(res, path)); | ||
} | ||
debug(`${res.request.method} ${res.request.href} ${res.statusCode}`); | ||
done(err); | ||
const result = Joi.validate(res, schema, joiOptions); | ||
if (result.error) { | ||
const detail = result.error.details[0]; | ||
const path = detail.path; | ||
return done(createError(detail, path, res)); | ||
} | ||
done(); | ||
}); | ||
}); | ||
}); | ||
if (this.after) { | ||
tasks.push(this.after); | ||
if (this.after) { | ||
tasks.push(this.after); | ||
} | ||
async.series(tasks, cb); | ||
} | ||
} | ||
async.series(tasks, cb); | ||
}; | ||
module.exports = Contract; |
@@ -1,8 +0,11 @@ | ||
var colors = require('chalk'); | ||
var util = require('util'); | ||
'use strict'; | ||
/* eslint-disable no-console */ | ||
const colors = require('chalk'); | ||
function addErrIndicies(results) { | ||
var errIndex = 1; | ||
let errIndex = 1; | ||
results.forEach(function (result) { | ||
results.forEach((result) => { | ||
if (result.err) { | ||
@@ -15,9 +18,9 @@ result.errIndex = errIndex++; | ||
function printResult(result) { | ||
var consumer = result.contract.consumer; | ||
var name = result.contract.name; | ||
const consumer = result.contract.consumer; | ||
const name = result.contract.name; | ||
if (result.err) { | ||
console.log(' ', colors.red(result.errIndex + ')', consumer, '–', name)); | ||
console.log(` ${colors.red(result.errIndex + ')', consumer, '–', name)}`); | ||
} else { | ||
console.log(' ', colors.green('✓'), colors.reset(consumer, '–', name)); | ||
console.log(` ${colors.green('✓')} ${colors.reset(consumer, '–', name)}`); | ||
} | ||
@@ -27,9 +30,9 @@ } | ||
function printFailure(failure, i) { | ||
var contract = failure.contract; | ||
var error = failure.err; | ||
const contract = failure.contract; | ||
const error = failure.err; | ||
console.log(' ', colors.reset(i + 1 + ')', contract.consumer, '–', contract.name)); | ||
console.log(' ', colors.red(error.message)); | ||
console.log(` ${colors.reset(i + 1 + ')', contract.consumer, '–', contract.name)}`); | ||
console.log(` ${colors.red(error.message)}`); | ||
if (error.detail) { | ||
console.log(' ', colors.gray(error.detail)); | ||
console.log(` ${colors.gray(error.detail)}`); | ||
} | ||
@@ -40,4 +43,4 @@ console.log(''); | ||
module.exports.print = function (data) { | ||
var failures = data.failures; | ||
var results = data.results; | ||
const failures = data.failures; | ||
const results = data.results; | ||
@@ -50,6 +53,6 @@ addErrIndicies(results); | ||
console.log('\n'); | ||
console.log(colors.green(util.format(' %d passing', data.totalPassed))); | ||
console.log(colors.green(` ${data.totalPassed} passing`)); | ||
if (failures.length > 0) { | ||
console.log(colors.red(util.format(' %d failing', data.totalFailed))); | ||
console.log(colors.red(` ${data.totalFailed} failing`)); | ||
console.log(''); | ||
@@ -56,0 +59,0 @@ failures.forEach(printFailure); |
@@ -1,13 +0,29 @@ | ||
var _ = require('lodash'); | ||
var FileHound = require('filehound'); | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var formatter = require('./formatter'); | ||
var validateContracts = require('./validator'); | ||
'use strict'; | ||
/* eslint-disable no-console */ | ||
const _ = require('lodash'); | ||
const FileHound = require('filehound'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const formatter = require('./formatter'); | ||
const validateContracts = require('./validator'); | ||
function isEmpty(obj) { | ||
return Object.keys(obj).length === 0; | ||
} | ||
function loadContracts(file) { | ||
const contract = require(file); | ||
if (isEmpty(contract)) { | ||
throw new Error(`No contract defined for '${file}'`); | ||
} | ||
return contract; | ||
} | ||
function validateFiles(files) { | ||
var contracts; | ||
let contracts; | ||
try { | ||
contracts = files.map(require); | ||
contracts = files.map(loadContracts); | ||
} catch (err) { | ||
@@ -19,8 +35,8 @@ err.message = 'Failed to load contract: ' + err.message; | ||
validateContracts(contracts, function (err, results) { | ||
var failures = _(results).filter('err').compact().value(); | ||
var totalCompleted = contracts.length; | ||
var totalFailed = failures.length; | ||
var totalPassed = totalCompleted - totalFailed; | ||
var exitCode = (failures.length > 0) ? 1 : 0; | ||
validateContracts(contracts, (err, results) => { | ||
const failures = _(results).filter('err').compact().value(); | ||
const totalCompleted = contracts.length; | ||
const totalFailed = failures.length; | ||
const totalPassed = totalCompleted - totalFailed; | ||
const exitCode = (failures.length > 0) ? 1 : 0; | ||
@@ -41,3 +57,3 @@ formatter.print({ | ||
if (files.length > 0) { | ||
files = files.map(function (file) { | ||
files = files.map((file) => { | ||
return path.join(process.cwd(), file); | ||
@@ -49,7 +65,4 @@ }); | ||
var dir = path.join(process.cwd(), 'contracts'); | ||
try { | ||
fs.statSync(dir); | ||
} catch (err) { | ||
const dir = path.join(process.cwd(), 'contracts'); | ||
if (!fs.existsSync(dir)) { | ||
console.error('No contracts directory found in the current working directory.'); | ||
@@ -62,3 +75,3 @@ return process.exit(1); | ||
.ext('js') | ||
.find(function (err, files) { | ||
.find((err, files) => { | ||
if (err) { | ||
@@ -68,4 +81,4 @@ console.error(err.message); | ||
} | ||
validateFiles(files); | ||
}); | ||
return validateFiles(files); | ||
}); | ||
}; |
@@ -1,5 +0,5 @@ | ||
var async = require('async'); | ||
const async = require('async'); | ||
function validateContract(contract, cb) { | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
cb(null, { | ||
@@ -12,4 +12,4 @@ contract: contract, | ||
module.exports = function (contracts, cb) { | ||
module.exports = (contracts, cb) => { | ||
async.mapSeries(contracts, validateContract, cb); | ||
}; |
{ | ||
"name": "consumer-contracts", | ||
"version": "1.5.0", | ||
"version": "3.0.1", | ||
"description": "Consumer driven contracts for Node.js", | ||
@@ -8,3 +8,3 @@ "main": "index.js", | ||
"test": "mocha", | ||
"lint": "jshint .", | ||
"lint": "eslint .", | ||
"posttest": "npm run lint", | ||
@@ -16,2 +16,5 @@ "coverage": "istanbul cover _mocha -- -R dot" | ||
}, | ||
"engines": { | ||
"node": ">4.0.0" | ||
}, | ||
"preferGlobal": true, | ||
@@ -26,3 +29,3 @@ "repository": { | ||
], | ||
"author": "robin@robinmurphy.co.uk", | ||
"author": "ibl-team@lists.forge.bbc.co.uk", | ||
"license": "MIT", | ||
@@ -46,4 +49,6 @@ "bugs": { | ||
"codeclimate-test-reporter": "^0.4.0", | ||
"eslint": "^2.10.2", | ||
"eslint-config-iplayer-es6": "^2.0.0", | ||
"eslint-plugin-mocha": "^2.2.0", | ||
"istanbul": "^0.4.2", | ||
"jshint": "^2.8.0", | ||
"mocha": "^3.0.1", | ||
@@ -53,16 +58,5 @@ "nock": "^9.0.2", | ||
}, | ||
"jshintConfig": { | ||
"quotmark": "single", | ||
"unused": true, | ||
"undef": true, | ||
"node": true, | ||
"globals": { | ||
"describe": false, | ||
"it": false, | ||
"before": false, | ||
"beforeEach": false, | ||
"after": false, | ||
"afterEach": false | ||
} | ||
"eslintConfig": { | ||
"extends": "iplayer-es6" | ||
} | ||
} |
@@ -1,10 +0,10 @@ | ||
var Contract = require('../lib/contract'); | ||
var Joi = require('joi'); | ||
var assert = require('chai').assert; | ||
var nock = require('nock'); | ||
var sinon = require('sinon'); | ||
const Contract = require('../lib/contract'); | ||
const Joi = require('joi'); | ||
const assert = require('chai').assert; | ||
const nock = require('nock'); | ||
const sinon = require('sinon'); | ||
describe('Contract', function () { | ||
it('throws an error when the name is missing', function () { | ||
assert.throws(function () { | ||
describe('Contract', () => { | ||
it('throws an error when the name is missing', () => { | ||
assert.throws(() => { | ||
new Contract({ | ||
@@ -22,4 +22,4 @@ consumer: 'Consumer', | ||
it('throws an error when the consumer is missing', function () { | ||
assert.throws(function () { | ||
it('throws an error when the consumer is missing', () => { | ||
assert.throws(() => { | ||
new Contract({ | ||
@@ -37,4 +37,4 @@ name: 'Name', | ||
it('throws an error when the request is missing', function () { | ||
assert.throws(function () { | ||
it('throws an error when the request is missing', () => { | ||
assert.throws(() => { | ||
new Contract({ | ||
@@ -50,4 +50,4 @@ name: 'Name', | ||
it('throws an error when the response is missing', function () { | ||
assert.throws(function () { | ||
it('throws an error when the response is missing', () => { | ||
assert.throws(() => { | ||
new Contract({ | ||
@@ -62,4 +62,4 @@ name: 'Name', | ||
it('supports passing custom Joi options', function () { | ||
var options = { | ||
it('supports passing custom Joi options', () => { | ||
const options = { | ||
name: 'Name', | ||
@@ -80,3 +80,3 @@ consumer: 'Consumer', | ||
}; | ||
var contract = new Contract(options); | ||
const contract = new Contract(options); | ||
@@ -87,8 +87,8 @@ assert.equal(contract.joiOptions.just, options.joiOptions.just); | ||
describe('.validate', function () { | ||
beforeEach(function () { | ||
describe('.validate', () => { | ||
beforeEach(() => { | ||
nock.cleanAll(); | ||
}); | ||
it('does not return an error when the contract is valid', function (done) { | ||
it('does not return an error when the contract is valid', (done) => { | ||
nock('http://api.example.com').get('/').reply(200, { | ||
@@ -98,3 +98,3 @@ foo: 'bar' | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -116,3 +116,3 @@ consumer: 'Consumer', | ||
it('returns an error when the contract is broken', function (done) { | ||
it('returns an error when the contract is broken', (done) => { | ||
nock('http://api.example.com').get('/').reply(200, { | ||
@@ -122,3 +122,3 @@ bar: 'baz' | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -137,3 +137,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ok(err); | ||
@@ -146,6 +146,6 @@ assert.equal(err.message, 'Contract failed: "bar" must be a number'); | ||
it('returns an error when the request fails', function (done) { | ||
it('returns an error when the request fails', (done) => { | ||
nock('http://api.example.com').get('/').socketDelay(1000).reply(200, {}); | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -162,3 +162,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ok(err); | ||
@@ -170,3 +170,3 @@ assert.equal(err.message, 'Request failed: ESOCKETTIMEDOUT'); | ||
it('sets a user-agent header', function (done) { | ||
it('sets a user-agent header', (done) => { | ||
nock('http://api.example.com', { | ||
@@ -178,3 +178,3 @@ reqheaders: { | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -193,3 +193,3 @@ consumer: 'Consumer', | ||
it('supports passing a custom request client', function (done) { | ||
it('supports passing a custom request client', (done) => { | ||
nock('http://api.example.com', { | ||
@@ -202,3 +202,3 @@ reqheaders: { | ||
var client = require('request').defaults({ | ||
const client = require('request').defaults({ | ||
headers: { | ||
@@ -209,3 +209,3 @@ authorization: 'Bearer xxx' | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -222,3 +222,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ifError(err); | ||
@@ -229,8 +229,8 @@ done(); | ||
it('runs the before function before validating the contact', function (done) { | ||
var before = sinon.stub().yields(); | ||
it('runs the before before validating the contact', (done) => { | ||
const before = sinon.stub().yields(); | ||
nock('http://api.example.com').get('/').reply(200); | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -247,3 +247,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ifError(err); | ||
@@ -255,8 +255,8 @@ sinon.assert.called(before); | ||
it('returns an error when the before function fails', function (done) { | ||
var before = sinon.stub().yields(new Error('Setup error')); | ||
it('returns an error when the before function fails', (done) => { | ||
const before = sinon.stub().yields(new Error('Setup error')); | ||
nock('http://api.example.com').get('/').reply(200); | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -273,3 +273,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ok(err); | ||
@@ -281,9 +281,8 @@ assert.equal(err.message, 'Setup error'); | ||
it('runs the after function after validating the contact', (done) => { | ||
const after = sinon.stub().yields(); | ||
it('runs the after function after validating the contact', function (done) { | ||
var after = sinon.stub().yields(); | ||
nock('http://api.example.com').get('/').reply(200); | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -300,3 +299,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ifError(err); | ||
@@ -308,8 +307,8 @@ sinon.assert.called(after); | ||
it('returns an error when the after function fails', function (done) { | ||
var after = sinon.stub().yields(new Error('Cleanup error')); | ||
it('returns an error when the after function fails', (done) => { | ||
const after = sinon.stub().yields(new Error('Cleanup error')); | ||
nock('http://api.example.com').get('/').reply(200); | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -326,3 +325,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ok(err); | ||
@@ -334,3 +333,3 @@ assert.equal(err.message, 'Cleanup error'); | ||
it('applies any custom Joi options', function (done) { | ||
it('applies any custom Joi options', (done) => { | ||
nock('http://api.example.com').get('/').reply(200, { | ||
@@ -341,3 +340,3 @@ bar: 'baz', | ||
var contract = new Contract({ | ||
const contract = new Contract({ | ||
name: 'Name', | ||
@@ -359,3 +358,3 @@ consumer: 'Consumer', | ||
contract.validate(function (err) { | ||
contract.validate((err) => { | ||
assert.ok(err); | ||
@@ -362,0 +361,0 @@ assert.equal(err.message, 'Contract failed: "baz" is not allowed'); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32924
16
507
9
2