committed by
GitHub
parent
36163f4822
commit
ed3cca0fe6
@ -3,15 +3,20 @@
|
||||
const t = require('tap')
|
||||
const semver = require('semver')
|
||||
const workq = require('workq')
|
||||
const helper = require('./helper')
|
||||
const { ConfigurationError } = require('../../lib/errors')
|
||||
|
||||
const { delve } = helper
|
||||
|
||||
const supportedFeatures = [
|
||||
'gtelte',
|
||||
'regex',
|
||||
'benchmark',
|
||||
'stash_in_path',
|
||||
'groovy_scripting',
|
||||
'headers'
|
||||
'headers',
|
||||
'transform_and_set',
|
||||
'catch_unauthorized'
|
||||
]
|
||||
|
||||
function TestRunner (opts) {
|
||||
@ -25,6 +30,7 @@ function TestRunner (opts) {
|
||||
this.response = null
|
||||
this.stash = new Map()
|
||||
this.tap = opts.tap || t
|
||||
this.isPlatinum = opts.isPlatinum
|
||||
this.q = opts.q || workq()
|
||||
}
|
||||
|
||||
@ -55,14 +61,148 @@ TestRunner.prototype.cleanup = function (q, done) {
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.snapshot.delete({ repository: '*', snapshot: '*' }, { ignore: 404 }, err => {
|
||||
this.tap.error(err, 'should not error:snapshot.delete')
|
||||
this.tap.error(err, 'should not error: snapshot.delete')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.snapshot.deleteRepository({ repository: '*' }, { ignore: 404 }, err => {
|
||||
this.tap.error(err, 'should not error:snapshot.deleteRepository')
|
||||
this.tap.error(err, 'should not error: snapshot.deleteRepository')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs some additional API calls to prepare ES for the Platinum test,
|
||||
* This set of calls should be executed before the final clenup.
|
||||
* @param {queue}
|
||||
* @param {function} done
|
||||
*/
|
||||
TestRunner.prototype.cleanupPlatinum = function (q, done) {
|
||||
this.tap.comment('Platinum Cleanup')
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.security.getRole((err, { body }) => {
|
||||
this.tap.error(err, 'should not error: security.getRole')
|
||||
const roles = Object.keys(body).filter(n => helper.esDefaultRoles.indexOf(n) === -1)
|
||||
helper.runInParallel(
|
||||
this.client, 'security.deleteRole',
|
||||
roles.map(r => ({ name: r, refresh: 'wait_for' }))
|
||||
)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: security.deleteRole'))
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.security.getUser((err, { body }) => {
|
||||
this.tap.error(err, 'should not error: security.getUser')
|
||||
const users = Object.keys(body).filter(n => helper.esDefaultUsers.indexOf(n) === -1)
|
||||
helper.runInParallel(
|
||||
this.client, 'security.deleteUser',
|
||||
users.map(r => ({ username: r, refresh: 'wait_for' }))
|
||||
)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: security.deleteUser'))
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.security.getPrivileges((err, { body }) => {
|
||||
this.tap.error(err, 'should not error: security.getPrivileges')
|
||||
const privileges = []
|
||||
Object.keys(body).forEach(app => {
|
||||
Object.keys(body[app]).forEach(priv => {
|
||||
privileges.push({
|
||||
name: body[app][priv].name,
|
||||
application: body[app][priv].application,
|
||||
refresh: 'wait_for'
|
||||
})
|
||||
})
|
||||
})
|
||||
helper.runInParallel(this.client, 'security.deletePrivileges', privileges)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: security.deletePrivileges'))
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.ml.stopDatafeed({ datafeedId: '*', force: true }, err => {
|
||||
this.tap.error(err, 'should not error: ml.stopDatafeed')
|
||||
this.client.ml.getDatafeeds({ datafeedId: '*' }, (err, { body }) => {
|
||||
this.tap.error(err, 'should error: not ml.getDatafeeds')
|
||||
const feeds = body.datafeeds.map(f => f.datafeed_id)
|
||||
helper.runInParallel(
|
||||
this.client, 'ml.deleteDatafeed',
|
||||
feeds.map(f => ({ datafeedId: f }))
|
||||
)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: ml.deleteDatafeed'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.ml.closeJob({ jobId: '*', force: true }, err => {
|
||||
this.tap.error(err, 'should not error: ml.closeJob')
|
||||
this.client.ml.getJobs({ jobId: '*' }, (err, { body }) => {
|
||||
this.tap.error(err, 'should not error: ml.getJobs')
|
||||
const jobs = body.jobs.map(j => j.job_id)
|
||||
helper.runInParallel(
|
||||
this.client, 'ml.deleteJob',
|
||||
jobs.map(j => ({ jobId: j, waitForCompletion: true, force: true }))
|
||||
)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: ml.deleteJob'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.xpack.rollup.getJobs({ id: '_all' }, (err, { body }) => {
|
||||
this.tap.error(err, 'should not error: rollup.getJobs')
|
||||
const jobs = body.jobs.map(j => j.config.id)
|
||||
helper.runInParallel(
|
||||
this.client, 'xpack.rollup.stopJob',
|
||||
jobs.map(j => ({ id: j, waitForCompletion: true }))
|
||||
)
|
||||
.then(() => helper.runInParallel(
|
||||
this.client, 'xpack.rollup.deleteJob',
|
||||
jobs.map(j => ({ id: j }))
|
||||
))
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: rollup.stopJob/deleteJob'))
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.tasks.list((err, { body }) => {
|
||||
this.tap.error(err, 'should not error: tasks.list')
|
||||
const tasks = Object.keys(body.nodes)
|
||||
.reduce((acc, node) => {
|
||||
const { tasks } = body.nodes[node]
|
||||
Object.keys(tasks).forEach(id => {
|
||||
if (tasks[id].cancellable) acc.push(id)
|
||||
})
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
helper.runInParallel(
|
||||
this.client, 'tasks.cancel',
|
||||
tasks.map(id => ({ taskId: id }))
|
||||
)
|
||||
.then(() => done())
|
||||
.catch(err => this.tap.error(err, 'should not error: tasks.cancel'))
|
||||
})
|
||||
})
|
||||
|
||||
q.add((q, done) => {
|
||||
this.client.indices.delete({ index: '.ml-*' }, { ignore: 404 }, err => {
|
||||
this.tap.error(err, 'should not error: indices.delete (ml indices)')
|
||||
done()
|
||||
})
|
||||
})
|
||||
@ -77,7 +217,7 @@ TestRunner.prototype.cleanup = function (q, done) {
|
||||
* - the actual test
|
||||
* - teardown
|
||||
* - cleanup
|
||||
* Internally uses a queue to guarantee the order of the test sections.
|
||||
* Internally uses a queue to guarantee the order of the test sections.
|
||||
* @param {object} setup (null if not needed)
|
||||
* @param {object} test
|
||||
* @oaram {object} teardown (null if not needed)
|
||||
@ -92,6 +232,20 @@ TestRunner.prototype.run = function (setup, test, teardown, end) {
|
||||
return end()
|
||||
}
|
||||
|
||||
if (this.isPlatinum) {
|
||||
this.tap.comment('Creating x-pack user')
|
||||
// Some platinum test requires this user
|
||||
this.q.add((q, done) => {
|
||||
this.client.security.putUser({
|
||||
username: 'x_pack_rest_user',
|
||||
body: { password: 'x-pack-test-password', roles: ['superuser'] }
|
||||
}, (err, { body }) => {
|
||||
this.tap.error(err, 'should not error: security.putUser')
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (setup) {
|
||||
this.q.add((q, done) => {
|
||||
this.exec('Setup', setup, q, done)
|
||||
@ -108,6 +262,12 @@ TestRunner.prototype.run = function (setup, test, teardown, end) {
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isPlatinum) {
|
||||
this.q.add((q, done) => {
|
||||
this.cleanupPlatinum(q, done)
|
||||
})
|
||||
}
|
||||
|
||||
this.q.add((q, done) => {
|
||||
this.cleanup(q, done)
|
||||
})
|
||||
@ -211,11 +371,33 @@ TestRunner.prototype.fillStashedValues = function (obj) {
|
||||
// iterate every key of the object
|
||||
for (const key in obj) {
|
||||
const val = obj[key]
|
||||
// if the key value is a string, and the string includes '${'
|
||||
// that we must update the content of '${...}'.
|
||||
// eg: 'Basic ${auth}' we search the stahed value 'auth'
|
||||
// and the resulting value will be 'Basic valueOfAuth'
|
||||
if (typeof val === 'string' && val.includes('${')) {
|
||||
const start = val.indexOf('${')
|
||||
const end = val.indexOf('}', val.indexOf('${'))
|
||||
const stashedKey = val.slice(start + 2, end)
|
||||
const stashed = this.stash.get(stashedKey)
|
||||
obj[key] = val.slice(0, start) + stashed + val.slice(end + 1)
|
||||
continue
|
||||
}
|
||||
// handle json strings, eg: '{"hello":"$world"}'
|
||||
if (typeof val === 'string' && val.includes('"$')) {
|
||||
const start = val.indexOf('"$')
|
||||
const end = val.indexOf('"', start + 1)
|
||||
const stashedKey = val.slice(start + 2, end)
|
||||
const stashed = '"' + this.stash.get(stashedKey) + '"'
|
||||
obj[key] = val.slice(0, start) + stashed + val.slice(end + 1)
|
||||
continue
|
||||
}
|
||||
// if the key value is a string, and the string includes '$'
|
||||
// we run the "update value" code
|
||||
if (typeof val === 'string' && val.includes('$')) {
|
||||
// update the key value
|
||||
obj[key] = getStashedValues.call(this, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// go deep in the object
|
||||
@ -261,6 +443,26 @@ TestRunner.prototype.set = function (key, name) {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a given transformation and stashes the result.
|
||||
* @param {string} the name to identify the stashed value
|
||||
* @param {string} the transformation function as string
|
||||
* @returns {TestRunner}
|
||||
*/
|
||||
TestRunner.prototype.transform_and_set = function (name, transform) {
|
||||
if (/base64EncodeCredentials/.test(transform)) {
|
||||
const [user, password] = transform
|
||||
.slice(transform.indexOf('(') + 1, -1)
|
||||
.replace(/ /g, '')
|
||||
.split(',')
|
||||
const userAndPassword = `${delve(this.response, user)}:${delve(this.response, password)}`
|
||||
this.stash.set(name, Buffer.from(userAndPassword).toString('base64'))
|
||||
} else {
|
||||
throw new Error(`Unknown transform: '${transform}'`)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a client command
|
||||
* @param {object} the action to perform
|
||||
@ -353,7 +555,15 @@ TestRunner.prototype.exec = function (name, actions, q, done) {
|
||||
if (action.set) {
|
||||
q.add((q, done) => {
|
||||
const key = Object.keys(action.set)[0]
|
||||
this.set(key, action.set[key])
|
||||
this.set(this.fillStashedValues(key), action.set[key])
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
if (action.transform_and_set) {
|
||||
q.add((q, done) => {
|
||||
const key = Object.keys(action.transform_and_set)[0]
|
||||
this.transform_and_set(key, action.transform_and_set[key])
|
||||
done()
|
||||
})
|
||||
}
|
||||
@ -423,8 +633,12 @@ TestRunner.prototype.exec = function (name, actions, q, done) {
|
||||
q.add((q, done) => {
|
||||
const key = Object.keys(action.length)[0]
|
||||
this.length(
|
||||
delve(this.response, this.fillStashedValues(key)),
|
||||
this.fillStashedValues(action.length)[key]
|
||||
key === '$body' || key === ''
|
||||
? this.response
|
||||
: delve(this.response, this.fillStashedValues(key)),
|
||||
key === '$body'
|
||||
? action.length[key]
|
||||
: this.fillStashedValues(action.length)[key]
|
||||
)
|
||||
done()
|
||||
})
|
||||
@ -694,20 +908,6 @@ function getSkip (arr) {
|
||||
return null
|
||||
}
|
||||
|
||||
// code from https://github.com/developit/dlv
|
||||
// needed to support an edge case: `a\.b`
|
||||
// where `a.b` is a single field: { 'a.b': true }
|
||||
function delve (obj, key, def, p) {
|
||||
p = 0
|
||||
// handle the key with a dot inside that is not a part of the path
|
||||
// and removes the backslashes from the key
|
||||
key = key.split
|
||||
? key.split(/(?<!\\)\./g).map(k => k.replace(/\\/g, ''))
|
||||
: key.replace(/\\/g, '')
|
||||
while (obj && p < key.length) obj = obj[key[p++]]
|
||||
return (obj === undefined || p < key.length) ? def : obj
|
||||
}
|
||||
|
||||
// Gets two *maybe* numbers and returns two valida numbers
|
||||
// it throws if one or both are not a valid number
|
||||
// the returned value is an array with the new values
|
||||
|
||||
Reference in New Issue
Block a user