actionhero
Version:
actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks
482 lines (430 loc) • 15.5 kB
JavaScript
var chai = require('chai')
var dirtyChai = require('dirty-chai')
var expect = chai.expect
chai.use(dirtyChai)
let path = require('path')
var ActionheroPrototype = require(path.join(__dirname, '/../../actionhero.js'))
var actionhero = new ActionheroPrototype()
var api
describe('Core: API', () => {
before((done) => {
actionhero.start((error, a) => {
expect(error).to.be.null()
api = a
done()
})
})
after((done) => {
actionhero.stop(() => {
done()
})
})
it('should have an api object with proper parts', (done) => {
[
api.actions.actions,
api.actions.versions,
api.actions.actions.cacheTest['1'],
api.actions.actions.randomNumber['1'],
api.actions.actions.status['1']
].forEach((item) => {
expect(item).to.be.instanceof(Object)
});
[
api.actions.actions.cacheTest['1'].run,
api.actions.actions.randomNumber['1'].run,
api.actions.actions.status['1'].run
].forEach((item) => {
expect(item).to.be.instanceof(Function)
});
[
api.actions.actions.randomNumber['1'].name,
api.actions.actions.randomNumber['1'].description
].forEach((item) => {
expect(typeof item).to.equal('string')
})
expect(api.config).to.be.instanceof(Object)
done()
})
it('should have loaded postVariables properly', (done) => {
[
'file',
'callback',
'action',
'apiVersion',
'key', // from cacheTest action
'value' // from cacheTest action
].forEach((item) => {
expect(api.params.postVariables.indexOf(item) >= 0).to.equal(true)
})
done()
})
describe('api versions', () => {
before((done) => {
api.actions.versions.versionedAction = [1, 2, 3]
api.actions.actions.versionedAction = {
'1': {
name: 'versionedAction',
description: 'I am a test',
version: 1,
outputExample: {},
run: function (api, connection, next) {
connection.response.version = 1
next(connection, true)
}
},
'2': {
name: 'versionedAction',
description: 'I am a test',
version: 2,
outputExample: {},
run: function (api, connection, next) {
connection.response.version = 1
next(connection, true)
}
},
'3': {
name: 'versionedAction',
description: 'I am a test',
version: 3,
outputExample: {},
run: (api, data, next) => {
data.response.version = 1
var error = {
'a': {'complex': 'error'}
}
next(error)
}
}
}
done()
})
after((done) => {
delete api.actions.actions.versionedAction
delete api.actions.versions.versionedAction
done()
})
it('will default actions to version 1 when no version is provided by the defintion', (done) => {
api.specHelper.runAction('randomNumber', (response) => {
expect(response.requesterInformation.receivedParams.apiVersion).to.equal(1)
done()
})
})
it('can specify an apiVersion', (done) => {
api.specHelper.runAction('versionedAction', {apiVersion: 1}, (response) => {
expect(response.requesterInformation.receivedParams.apiVersion).to.equal(1)
api.specHelper.runAction('versionedAction', {apiVersion: 2}, (response) => {
expect(response.requesterInformation.receivedParams.apiVersion).to.equal(2)
done()
})
})
})
it('will default clients to the latest version of the action', (done) => {
api.specHelper.runAction('versionedAction', (response) => {
expect(response.requesterInformation.receivedParams.apiVersion).to.equal(3)
done()
})
})
it('will fail on a missing action + version', (done) => {
api.specHelper.runAction('versionedAction', {apiVersion: 10}, (response) => {
expect(response.error).to.equal('Error: unknown action or invalid apiVersion')
done()
})
})
it('can return complex error responses', (done) => {
api.specHelper.runAction('versionedAction', {apiVersion: 3}, (response) => {
expect(response.error.a.complex).to.equal('error')
done()
})
})
})
describe('Action Params', () => {
before((done) => {
api.actions.versions.testAction = [1]
api.actions.actions.testAction = {
'1': {
name: 'testAction',
description: 'this action has some required params',
version: 1,
inputs: {
requiredParam: {required: true},
optionalParam: {required: false},
fancyParam: {
required: false,
default: () => { return 'abc123' },
validator: function (s) {
if (s === 'abc123') { return true } else { return 'fancyParam should be "abc123". so says ' + this.id }
},
formatter: function (s) {
return String(s)
}
}
},
run: function (api, connection, next) {
connection.response.params = connection.params
next(connection, true)
}
}
}
done()
})
after((done) => {
delete api.actions.actions.testAction
delete api.actions.versions.testAction
api.config.general.missingParamChecks = [null, '', undefined]
done()
})
it('correct params that are falsey (false, []) should be allowed', (done) => {
api.specHelper.runAction('testAction', {requiredParam: false}, (response) => {
expect(response.params.requiredParam).to.equal(false)
api.specHelper.runAction('testAction', {requiredParam: []}, (response) => {
expect(response.params.requiredParam).to.have.length(0)
done()
})
})
})
it('will fail for missing or empty string params', (done) => {
api.specHelper.runAction('testAction', {requiredParam: ''}, (response) => {
expect(response.error).to.contain('required parameter for this action')
api.specHelper.runAction('testAction', {}, (response) => {
expect(response.error).to.match(/requiredParam is a required parameter for this action/)
done()
})
})
})
it('correct params respect config options', (done) => {
api.config.general.missingParamChecks = [undefined]
api.specHelper.runAction('testAction', {requiredParam: ''}, (response) => {
expect(response.params.requiredParam).to.equal('')
api.specHelper.runAction('testAction', {requiredParam: null}, (response) => {
expect(response.params.requiredParam).to.be.null()
done()
})
})
})
it('will set a default when params are not provided', (done) => {
api.specHelper.runAction('testAction', {requiredParam: true}, (response) => {
expect(response.params.fancyParam).to.equal('abc123')
done()
})
})
it('will use validator if provided', (done) => {
api.specHelper.runAction('testAction', {requiredParam: true, fancyParam: 123}, (response) => {
expect(response.error).to.match(/Error: fancyParam should be "abc123"/)
done()
})
})
it('validator will have the API object in scope as this', (done) => {
api.specHelper.runAction('testAction', {requiredParam: true, fancyParam: 123}, (response) => {
expect(response.error).to.match(new RegExp(api.id))
done()
})
})
it('will use formatter if provided (and still use validator)', (done) => {
api.specHelper.runAction('testAction', {requiredParam: true, fancyParam: 123}, (response) => {
expect(response.requesterInformation.receivedParams.fancyParam).to.equal('123')
done()
})
})
it('will filter params not set in the target action or global safelist', (done) => {
api.specHelper.runAction('testAction', {requiredParam: true, sleepDuration: true}, (response) => {
expect(response.requesterInformation.receivedParams.requiredParam).to.be.ok()
expect(response.requesterInformation.receivedParams.sleepDuration).to.not.exist()
done()
})
})
})
describe('Action Params schema type', () => {
before(() => {
api.actions.versions.testAction = [1]
api.actions.actions.testAction = {
'1': {
name: 'testAction',
description: 'this action has some required params',
version: 1,
inputs: {
schemaParam: {
schema: {
requiredParam: {required: true},
optionalParam: {required: false},
fancyParam: {
required: false,
default: () => { return 'abc123' },
validator: function (s) {
if (s === 'abc123') { return true } else { return 'fancyParam should be "abc123". so says ' + this.id }
},
formatter: function (s) {
return String(s)
}
}
}
}
},
run: function (api, connection, next) {
connection.response.params = connection.params
next(connection, true)
}
}
}
})
after(() => {
delete api.actions.actions.testAction
delete api.actions.versions.testAction
api.config.general.missingParamChecks = [null, '', undefined]
})
it('correct params that are falsey (false, []) should be allowed', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: false}}, (response) => {
expect(response.params.schemaParam.requiredParam).to.equal(false)
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: []}}, (response) => {
expect(response.params.schemaParam.requiredParam).to.have.length(0)
done()
})
})
})
it('will fail for missing or empty string params', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: ''}}, (response) => {
expect(response.error).to.contain('schemaParam.requiredParam is a required parameter for this action')
api.specHelper.runAction('testAction', {schemaParam: {}}, (response) => {
expect(response.error).to.contain('schemaParam.requiredParam is a required parameter for this action')
done()
})
})
})
it('correct params respect config options', (done) => {
api.config.general.missingParamChecks = [undefined]
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: ''}}, (response) => {
expect(response.params.schemaParam.requiredParam).to.equal('')
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: null}}, (response) => {
expect(response.params.schemaParam.requiredParam).to.be.null()
done()
})
})
})
it('will set a default when params are not provided', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: true}}, (response) => {
expect(response.params.schemaParam.fancyParam).to.equal('abc123')
done()
})
})
it('will use validator if provided', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: true, fancyParam: 123}}, (response) => {
expect(response.error).to.match(/Error: fancyParam should be "abc123"/)
done()
})
})
it('validator will have the API object in scope as this', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: true, fancyParam: 123}}, (response) => {
expect(response.error).to.match(new RegExp(api.id))
done()
})
})
it('will use formatter if provided (and still use validator)', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: true, fancyParam: 123}}, (response) => {
expect(response.requesterInformation.receivedParams.schemaParam.fancyParam).to.equal('123')
done()
})
})
it('will filter params not set in the target action or global safelist', (done) => {
api.specHelper.runAction('testAction', {schemaParam: {requiredParam: true, sleepDuration: true}}, (response) => {
expect(response.requesterInformation.receivedParams.schemaParam.requiredParam).to.be.ok()
expect(response.requesterInformation.receivedParams.schemaParam.sleepDuration).to.not.exist()
done()
})
})
})
describe('named action validations', () => {
before((done) => {
api.validators = {
validator1: function (param) {
if (typeof param !== 'string') { return new Error('only strings') }
return true
},
validator2: function (param) {
if (param !== 'correct') { return new Error('that is not correct') }
return true
}
}
api.actions.versions.testAction = [1]
api.actions.actions.testAction = {
'1': {
name: 'testAction',
description: 'I am a test',
inputs: {
a: {
validator: ['api.validators.validator1', 'api.validators.validator2']
}
},
run: (api, data, next) => {
next()
}
}
}
done()
})
after((done) => {
delete api.actions.versions.testAction
delete api.actions.actions.testAction
delete api.validators
done()
})
it('runs validator arrays in the proper order', (done) => {
api.specHelper.runAction('testAction', {a: 6}, (response) => {
expect(response.error).to.equal('Error: only strings')
done()
})
})
it('runs more than 1 validator', (done) => {
api.specHelper.runAction('testAction', {a: 'hello'}, (response) => {
expect(response.error).to.equal('Error: that is not correct')
done()
})
})
it('succeeds multiple validators', (done) => {
api.specHelper.runAction('testAction', {a: 'correct'}, (response) => {
expect(response.error).to.not.exist()
done()
})
})
})
describe('named action formatters', () => {
before((done) => {
api.formatters = {
formatter1: function (param) {
return '*' + param + '*'
},
formatter2: function (param) {
return '~' + param + '~'
}
}
api.actions.versions.testAction = [1]
api.actions.actions.testAction = {
'1': {
name: 'testAction',
description: 'I am a test',
inputs: {
a: {
formatter: ['api.formatters.formatter1', 'api.formatters.formatter2']
}
},
run: (api, data, next) => {
data.response.a = data.params.a
next()
}
}
}
done()
})
after((done) => {
delete api.actions.versions.testAction
delete api.actions.actions.testAction
delete api.formatters
done()
})
it('runs formatter arrays in the proper order', (done) => {
api.specHelper.runAction('testAction', {a: 6}, (response) => {
expect(response.a).to.equal('~*6*~')
done()
})
})
})
})