Major updates for testing and grunt, jenkins tests are now powered by the jenkins.sh script in the scripts directory.

This commit is contained in:
Spencer Alger
2013-12-12 15:39:42 -07:00
parent 270763e0a7
commit 345ac776ef
68 changed files with 1628 additions and 8790 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ test/integration/yaml_suite/log
## generated files
scripts/last_rest_spec_update.sha
test/integration/yaml_suite/yaml_tests.json
test/browser_integration/yaml_tests.js
test-output-*.xml
coverage.html

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/rest-api-spec"]
path = src/rest-api-spec
url = git@github.com:elasticsearch/elasticsearch-rest-api-spec.git

View File

View File

@ -66,3 +66,21 @@ bower install elasticsearch-jquery
- [Extending Core Components](http://spenceralger.github.io/elasticsearch-js/index.html#extending)
- [Logging](http://spenceralger.github.io/elasticsearch-js/index.html#logging)
- [Contributing](http://spenceralger.github.io/elasticsearch-js/index.html#contributing)
## License
This software is licensed under the Apache 2 license, quoted below.
Copyright (c) 2013 Elasticsearch <http://www.elasticsearch.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,5 +1,5 @@
module.exports = function (grunt) {
grunt.registerTask('build', [
grunt.registerTask('browser_clients_build', [
'clean:dist',
'browserify',
'uglify:dist',

View File

@ -0,0 +1,30 @@
module.exports = function (grunt) {
grunt.registerTask('export_client', function (build, outDir) {
var path = require('path');
grunt.config.set('copy.export_client', {
expand: true,
cwd: './dist/',
src: 'elasticsearch' + (build ? '.' + build : '') + '{.min,}.js',
dest: outDir,
rename: function (dest, src) {
return path.join(dest, 'elasticsearch' + (~src.indexOf('.min') ? '.min' : '') + '.js');
}
});
this.requires('build');
grunt.task.run([
'copy:export_client'
]);
});
grunt.registerTask('export_all_clients', function () {
grunt.task.run([
'build',
'export_client:angular:../bower-elasticsearch-angular',
'export_client::../bower-elasticsearch-browser',
'export_client:jquery:../bower-elasticsearch-jquery'
]);
});
};

View File

@ -0,0 +1,10 @@
module.exports = function (grunt) {
grunt.registerTask('browser_clients_publish', [
'build_browser_clients',
'compress:dist_zip',
'compress:dist_tarball',
's3:latest'
]);
};

View File

@ -0,0 +1,10 @@
module.exports = function (grunt) {
grunt.registerTask('browser_clients_release', [
'build_browser_clients',
'compress:dist_zip',
'compress:dist_tarball',
's3:release'
]);
};

View File

@ -1,4 +1,18 @@
module.exports = {
yaml_suite: {
options: {
external: [
'optimist'
],
ignore: [
'test/integration/yaml_suite/reporter',
'src/elasticsearch.js'
]
},
files: {
'test/integration/browser_yaml_suite/yaml_tests.js': 'test/integration/yaml_suite/index.js'
}
},
browser_client: {
files: {
'<%= distDir %>/elasticsearch.js': 'src/elasticsearch.js'

14
grunt/config/compress.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
dist_zip: {
src: '<%= distDir %>/*.js',
options: {
archive: '<%= distDir %>/archives/elasticsearch-js.zip'
}
},
dist_tarball: {
src: '<%= distDir %>/*.js',
options: {
archive: '<%= distDir %>/archives/elasticsearch-js.tar.gz'
}
}
};

View File

@ -4,6 +4,7 @@ module.exports = {
'src/**/*.js',
'scripts/**/*.js',
'test/**/*.js -test/browser_integration/yaml_tests.js',
'grunt/**/*.js',
'Gruntfile.js'
],
options: {

8
grunt/config/mocha.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
unit: {
src: ['test/unit/test_*.js'],
reporter: 'XUnit',
dest: './test-output-phantom-unit.xml',
run: true
}
};

43
grunt/config/run.js Normal file
View File

@ -0,0 +1,43 @@
module.exports = {
generate: {
exec: 'node scripts/generate'
},
unit_tests: {
exec: 'node scripts/run_tests --unit --no-browsers',
options: {
passArgs: [
'port',
'host'
]
}
},
integration_tests: {
exec: 'node scripts/run_tests --integration --no-browsers',
options: {
passArgs: [
'port',
'host'
]
}
},
browser_unit_tests: {
exec: 'node scripts/run_tests --unit --no-server',
options: {
cwd: '.',
passArgs: [
'port',
'host'
]
}
},
browser_integration_tests: {
exec: 'node scripts/run_tests --integration --no-server',
options: {
cwd: '.',
passArgs: [
'port',
'host'
]
}
}
};

View File

@ -1,17 +1,26 @@
var config = require('../../.aws-config.json');
var config = {};
try {
config = require('../../.aws-config.json');
} catch (e) {}
module.exports = {
options: {
key: config.key,
secret: config.secret,
bucket: 'download.elasticsearch.org',
access: 'public-read'
access: 'public-read',
headers: {
'Content-Type': 'text/plain',
'X-Content-Type-Options': 'nosniff',
'Content-Disposition': 'attachment'
}
},
latest: {
upload: [
{
src: '<%= distDir %>/*.js',
src: '<%= distDir %>/archives/*',
dest: 'elasticsearch/elasticsearch-js/latest'
}
]
@ -20,7 +29,7 @@ module.exports = {
release: {
upload: [
{
src: '<%= distDir %>/*.js',
src: '<%= distDir %>/archives/*',
dest: 'elasticsearch/elasticsearch-js/<%= package.version %>'
}
]

View File

@ -2,7 +2,8 @@ module.exports = function (grunt) {
// Default task runs the build process.
grunt.registerTask('default', [
'build'
'run:generate',
'test'
]);
};

5
grunt/generate.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = function (grunt) {
grunt.registerTask('generate', [
'run:generate'
]);
};

View File

@ -1,8 +0,0 @@
module.exports = function (grunt) {
grunt.registerTask('publish', [
'build',
's3:latest'
]);
};

View File

@ -1,8 +0,0 @@
module.exports = function (grunt) {
grunt.registerTask('release', [
'build',
's3:release'
]);
};

15
grunt/test.js Normal file
View File

@ -0,0 +1,15 @@
module.exports = function (grunt) {
grunt.registerTask('test', [
'jshint',
'run:unit_tests',
'run:integration_tests'
]);
grunt.registerTask('browser_clients_test', [
'build',
'run:browser_unit_tests',
'run:browser_integration_tests'
]);
};

View File

@ -9,7 +9,8 @@
"version": "0.0.1",
"browser": {
"./src/lib/connectors/index.js": "./src/lib/connectors/browser_index.js",
"./src/lib/loggers/index.js": "./src/lib/loggers/browser_index.js"
"./src/lib/loggers/index.js": "./src/lib/loggers/browser_index.js",
"./test/mocks/server.js": "./test/mocks/browser_server.js"
},
"devDependencies": {
"tar": "~0.1.18",
@ -36,12 +37,17 @@
"mocha-lcov-reporter": "0.0.1",
"blanket": "~1.1.5",
"sinon": "~1.7.3",
"nock": "~0.23.0",
"nock": "git://github.com/spenceralger/nock.git#c5b07103eb3058035b99118d343ee0dfca666107",
"open": "0.0.4",
"testling": "git://github.com/spenceralger/testling.git",
"load-grunt-tasks": "~0.2.0",
"load-grunt-config": "~0.7.0",
"grunt-s3": "~0.2.0-alpha.3"
"grunt-s3": "~0.2.0-alpha.3",
"grunt-run": "~0.1.5",
"relative-fs": "0.0.1",
"grunt-contrib-compress": "~0.5.3",
"grunt-contrib-copy": "~0.4.1",
"grunt-mocha": "~0.4.7"
},
"license": "Apache 2.0",
"dependencies": {
@ -51,14 +57,12 @@
"chalk": "~0.3.0"
},
"repository": {
"type" : "git",
"url" : "http://github.com/elasticsearch/elasticsearch-js.git"
"type": "git",
"url": "http://github.com/elasticsearch/elasticsearch-js.git"
},
"scripts": {
"test": "node scripts/run_tests.js --integration --unit --browsers=chrome,safari,firefox,opera",
"coverage": "mocha test/unit/test_*.js --require blanket -R html-cov > coverage.html && open -a \"Google Chrome\" ./coverage.html",
"build_clients": "grunt",
"generate": "node scripts/generate/js_api && node scripts/generate/yaml_tests",
"test": "grunt test",
"coverage": "mocha test/unit/test_*.js --require blanket -R html-cov > coverage.html && open -a \"Google Chrome\"./coverage.html",
"blanket": {
"pattern": "src"
}

View File

@ -1,33 +0,0 @@
var path = require('path');
var fs = require('fs');
var argv = require('optimist')
.default({
verbose: false
})
.alias({
v: 'verbose'
})
.argv;
var steps = [];
['browser', 'jquery', 'angular'].forEach(function (build) {
if (!fs.existsSync('../bower-elasticsearch-' + build) ||
!fs.existsSync('../bower-elasticsearch-' + build + '/.git')
) {
throw new Error('Ensure that all of the bower repos are checked out next to this repo');
}
steps.push([
'run', {
cwd: '../bower-elasticsearch-' + build + '/',
cmd: 'npm',
args: ['run', 'generate']
}
]);
});
require('./_steps')(argv, steps);

View File

@ -1,49 +0,0 @@
var path = require('path');
var argv = require('optimist')
.default({
buildName: '',
outputDir: '.',
verbose: false
})
.alias({
b: 'buildName',
o: 'outputDir',
v: 'verbose'
})
.argv;
switch (argv.buildName) {
case 'jquery':
var buildFile = './dist/elasticsearch.jquery.js';
var minifiedBuildFile = './dist/elasticsearch.jquery.min.js';
break;
case 'angular':
var buildFile = './dist/elasticsearch.angular.js';
var minifiedBuildFile = './dist/elasticsearch.angular.min.js';
break;
default:
var buildFile = './dist/elasticsearch.js';
var minifiedBuildFile = './dist/elasticsearch.min.js';
break;
}
var outputFile = path.join(argv.outputDir, 'elasticsearch.js');
var minifiedOutputFile = path.join(argv.outputDir, 'elasticsearch.min.js');
require('./_steps')(argv, [
['run', {
cmd: 'npm',
args: ['run', 'build_clients']
}],
['copy', {
from: buildFile,
to: outputFile
}],
['copy', {
from: minifiedBuildFile,
to: minifiedOutputFile
}]
]);

View File

@ -14,7 +14,7 @@ var argv = require('optimist')
require('./_steps')(argv, [
['runInModule', {
cmd: 'node',
args: [path.join(__dirname, './generate/js_api'), '--force']
args: [path.join(__dirname, './generate'), '--force']
}],
['copy', {
from: path.join(__dirname, '../docs/_methods.jade'),

View File

@ -1,24 +0,0 @@
var force = process.env.FORCE || process.env.FORCE_GEN;
if (!force) {
var argv = require('optimist')
.options({
force: {
alias: 'f',
default: false,
boolean: true
}
});
if (process.env.npm_config_argv) {
// when called by NPM
argv = argv.parse(JSON.parse(process.env.npm_config_argv).original);
} else {
// when called directly
argv = argv.argv;
}
force = argv.force;
}
module.exports = force;

71
scripts/generate/index.js Normal file
View File

@ -0,0 +1,71 @@
var cp = require('child_process');
var async = require('async');
var argv = require('optimist')
.options({
force: {
alias: 'f',
default: false,
boolean: true
},
verbose: {
alias: 'v',
default: false,
boolean: true
},
api: {
default: true,
boolean: true
},
tests: {
default: true,
boolean: true
}
});
if (process.env.npm_config_argv) {
// when called by NPM
argv = argv.parse(JSON.parse(process.env.npm_config_argv).original);
} else {
// when called directly
argv = argv.argv;
}
if (!argv.force && process.env.FORCE || process.env.FORCE_GEN) {
argv.force = argv.f = process.env.FORCE || process.env.FORCE_GEN;
}
function updateSubmodules(done) {
cp.exec('git submodule update --init --recursive', function (err, stdout, stderr) {
stdout = stdout.trim();
stderr = stderr.trim();
if (err) {
done(new Error('Unable to update submodules: ' + err.message));
return;
} else if (argv.verbose && stdout) {
console.log(stdout);
}
if (stderr) {
console.error(stderr);
}
done();
});
}
updateSubmodules(function (err) {
if (err) {
throw err;
}
var tasks = [];
if (argv.api) {
tasks.push(require('./js_api'));
}
if (argv.tests) {
tasks.push(require('./yaml_tests'));
}
async.parallel(tasks, function () {});
});

344
scripts/generate/js_api.js Normal file
View File

@ -0,0 +1,344 @@
var aliases; // defined at the bottom of this file.
module.exports = function (done) {
/**
* Read the API actions form the rest-api-spec repo.
* @type {[type]}
*/
var _ = require('../../src/lib/utils');
var fs = require('relative-fs').relativeTo(__dirname);
var async = require('async');
var templates = require('./templates');
var castExistsRE = /exists/;
var usesBulkBodyRE = /^(bulk|msearch)$/;
var urlParamRE = /\{(\w+)\}/g;
var files; // populated in readSpecFiles
var apiSpec; // populated by parseSpecFiles
// generate the API
async.series([
readSpecFiles,
parseSpecFiles,
writeApiFile,
ensureDocsDir,
writeMethodList,
writeMethodDocs
], done);
function readSpecFiles(done) {
var apiDir = '../../src/rest-api-spec/api/';
files = fs.readdirSync(apiDir).map(function (filename) {
return require(apiDir + filename);
});
done();
}
function parseSpecFiles(done) {
var actions = [];
files.forEach(function (spec) {
__puke__transformSpec(spec).forEach(function (action) {
actions.push(action);
});
});
// collect the namespaces from the action locations
var namespaces = _.filter(_.map(actions, function (action) {
if (~action.location.indexOf('.')) {
var path = action.location.split('.').slice(0, -1);
_.pull(path, 'prototype');
return path.join('.');
}
}));
// seperate the proxy actions
var groups = _.groupBy(actions, function (action) {
return action.proxy ? 'proxies' : 'normal';
});
apiSpec = {
actions: groups.normal,
proxies: groups.proxies,
namespaces: _.unique(namespaces.sort(), true)
};
done();
}
function writeApiFile(done) {
var outputPath = require('path').join(__dirname, '../../src/lib/api.js');
console.log('writing', apiSpec.actions.length, 'api actions to', outputPath);
fs.writeFile(outputPath, templates.apiFile(apiSpec), done);
}
function ensureDocsDir(done) {
fs.stat('../../docs', function (err, stat) {
if (err) {
if (err.message.match(/enoent/i)) {
fs.mkdir('../../docs', done);
} else {
done(err);
}
} else if (stat.isDirectory()) {
done();
} else {
done(new Error('../../docs exists, but it is not a directory'));
}
});
}
function writeMethodList(done) {
fs.writeFile(
'../../docs/_method_list.jade',
templates.apiMethodList(apiSpec),
done
);
}
function writeMethodDocs(done) {
fs.writeFile(
'../../docs/_methods.jade',
templates.apiMethods(apiSpec),
done
);
}
function __puke__transformSpec(spec) {
var actions = [];
// itterate all of the specs within the file, should only be one
_.each(spec, function (def, name) {
//camelcase the name
name = _.map(name.split('.'), _.camelCase).join('.');
var steps = name.split('.');
function transformParamKeys(note, param, key) {
var cmlKey = _.camelCase(key);
if (cmlKey !== key) {
param.name = key;
}
note[cmlKey] = param;
}
def.url.params = _.transform(def.url.params, transformParamKeys, {});
def.url.parts = _.transform(def.url.parts, transformParamKeys, {});
var allParams = _.extend({}, def.url.params, def.url.parts);
var spec = {
name: name,
methods: _.map(def.methods, function (m) { return m.toUpperCase(); }),
params: def.url.params,
body: def.body || null,
path2lib: _.repeat('../', steps.length + 1) + 'lib/'
};
if (def.body && def.body.requires) {
spec.needBody = true;
}
if (usesBulkBodyRE.test(name)) {
spec.bulkBody = true;
}
if (castExistsRE.test(name)) {
spec.castExists = true;
}
var urls = _.difference(def.url.paths, aliases[name]);
urls = _.map(urls, function (url) {
var optionalVars = {};
var requiredVars = {};
var param;
var name;
var target;
var match;
if (url.charAt(0) !== '/') {
url = '/' + url;
}
while (match = urlParamRE.exec(url)) {
name = _.camelCase(match[1]);
param = def.url.parts[name] || {};
target = (param.required || !param.default) ? requiredVars : optionalVars;
target[name] = _.omit(param, 'required', 'description', 'name');
}
return _.omit({
fmt: url.replace(urlParamRE, function (full, match) {
return '<%=' + _.camelCase(match) + '%>';
}),
opt: _.size(optionalVars) ? optionalVars : null,
req: _.size(requiredVars) ? requiredVars : null,
sortOrder: _.size(requiredVars) * -1
}, function (v) {
return !v;
});
});
if (urls.length > 1) {
spec.urls = _.map(_.sortBy(urls, 'sortOrder'), function (url) {
return _.omit(url, 'sortOrder');
});
} else {
spec.url = urls[0];
}
spec.params = _.transform(spec.params, function (note, param, name) {
// param.name = name;
note[name] = _.pick(param, [
'type', 'default', 'options', 'required', 'name'
]);
}, {});
if (_.size(spec.params) === 0) {
delete spec.params;
}
// escape method names with "special" keywords
var location = spec.name.split('.').join('.prototype.')
.replace(/(^|\.)(delete|default)(\.|$)/g, '[\'$2\']');
var action = {
_methods: spec.methods,
spec: _.pick(spec, [
'params',
'url',
'urls',
'needBody',
'bulkBody',
'castExists',
'castNotFound'
]),
location: location,
docUrl: def.documentation,
name: spec.name,
allParams: allParams
};
function hasMethod(/* ...methods */) {
for (var i = 0; i < arguments.length; i++) {
if (~action._methods.indexOf(arguments[i])) {
continue;
} else {
return false;
}
}
return true;
}
function methodsAre(/* ...methods */) {
return hasMethod.apply(null, arguments) && arguments.length === action._methods.length;
}
var method;
if (action._methods.length === 1) {
method = action._methods[0];
} else {
// we need to define what the default method(s) will be
if (hasMethod('DELETE', 'POST')) {
method = 'POST';
}
else if (methodsAre('DELETE')) {
method = 'DELETE';
}
else if (methodsAre('POST', 'PUT')) {
method = action.name.match(/put/i) ? 'PUT' : 'POST';
}
else if (methodsAre('GET', 'POST')) {
method = 'POST';
}
else if (methodsAre('GET', 'HEAD')) {
if (action.spec.castExists) {
method = 'HEAD';
} else {
method = 'GET';
}
}
}
if (method) {
if (method !== 'GET') {
action.spec.method = method;
}
} else {
throw new Error('unable to pick a method for ' + JSON.stringify(action, null, ' '));
}
if (action.name === 'create') {
action.proxy = 'index';
action.transformBody = 'params.op_type = \'create\';';
}
actions.push(action);
});
return actions;
}
};
aliases = {
'cluster.nodeHotThreads': [
'/_cluster/nodes/hotthreads',
'/_cluster/nodes/hot_threads',
'/_nodes/hot_threads',
'/_cluster/nodes/{node_id}/hotthreads',
'/_cluster/nodes/{node_id}/hot_threads',
'/_nodes/{node_id}/hot_threads'
],
'cluster.nodeInfo': [
'/_cluster/nodes',
'/_nodes/settings',
'/_nodes/os',
'/_nodes/process',
'/_nodes/jvm',
'/_nodes/thread_pool',
'/_nodes/network',
'/_nodes/transport',
'/_nodes/http',
'/_nodes/plugin',
'/_cluster/nodes/{node_id}',
'/_nodes/{node_id}/settings',
'/_nodes/{node_id}/os',
'/_nodes/{node_id}/process',
'/_nodes/{node_id}/jvm',
'/_nodes/{node_id}/thread_pool',
'/_nodes/{node_id}/network',
'/_nodes/{node_id}/transport',
'/_nodes/{node_id}/http',
'/_nodes/{node_id}/plugin'
],
'cluster.nodeShutdown': [
'/_cluster/nodes/_shutdown'
],
'cluster.nodeStats': [
'/_cluster/nodes/stats',
'/_nodes/stats/{metric_family}',
'/_nodes/stats/indices/{metric}/{fields}',
'/_cluster/nodes/{node_id}/stats',
'/_nodes/{node_id}/stats/{metric_family}',
'/_nodes/{node_id}/stats/indices/{metric}/{fields}'
],
'get': [
'/{index}/{type}/{id}/_source'
],
'indices.deleteMapping': [
'/{index}/{type}/_mapping'
],
'indices.stats': [
'_stats/{metric_family}',
'/_stats/indexing',
'/_stats/indexing/{indexing_types}',
'/_stats/search/{search_groups}',
'/_stats/fielddata/{fields}',
'/{index}/_stats/{metric_family}',
'/{index}/_stats/indexing',
'/{index}/_stats/search/{search_groups}',
'/{index}/_stats/fielddata/{fields}'
],
'search': [
'/_search'
]
};

View File

@ -1,3 +0,0 @@
Generate the API using the es [REST api spec](https://github.com/elasticsearch/elasticsearch-rest-api-spec).
run by calling `npm run generate`. Force it to update, even if their has not been a new commit, but calling with `--force` or `-f`

View File

@ -1,240 +0,0 @@
var _ = require('../../../src/lib/utils');
var EventEmitter = require('events').EventEmitter;
var aliases = require('./aliases');
var castExistsRE = /exists/;
var usesBulkBodyRE = /^(bulk|msearch)$/;
var urlParamRE = /\{(\w+)\}/g;
var specCount = 0;
var actions = [];
var doneParsing = false;
require('../../get_spec')
.get('api/*.json')
.on('entry', transformFile)
.on('end', function () {
doneParsing = true;
if (actions.length === specCount) {
module.exports.emit('ready', actions);
}
});
function transformFile(entry) {
specCount++;
// itterate all of the specs within the file, should only be one
_.each(JSON.parse(entry.data), function (def, name) {
//camelcase the name
name = _.map(name.split('.'), _.camelCase).join('.');
var steps = name.split('.');
function transformParamKeys(note, param, key) {
var cmlKey = _.camelCase(key);
if (cmlKey !== key) {
param.name = key;
}
note[cmlKey] = param;
}
def.url.params = _.transform(def.url.params, transformParamKeys, {});
def.url.parts = _.transform(def.url.parts, transformParamKeys, {});
var allParams = _.extend({}, def.url.params, def.url.parts);
var spec = {
name: name,
methods: _.map(def.methods, function (m) { return m.toUpperCase(); }),
params: def.url.params,
body: def.body || null,
path2lib: _.repeat('../', steps.length + 1) + 'lib/'
};
if (def.body && def.body.requires) {
spec.needBody = true;
}
if (usesBulkBodyRE.test(name)) {
spec.bulkBody = true;
}
if (castExistsRE.test(name)) {
spec.castExists = true;
}
var urls = _.difference(def.url.paths, aliases[name]);
urls = _.map(urls, function (url) {
var optionalVars = {};
var requiredVars = {};
var param;
var name;
var target;
var match;
if (url.charAt(0) !== '/') {
url = '/' + url;
}
while (match = urlParamRE.exec(url)) {
name = _.camelCase(match[1]);
param = def.url.parts[name] || {};
target = (param.required || !param.default) ? requiredVars : optionalVars;
target[name] = _.omit(param, 'required', 'description', 'name');
}
return _.omit({
fmt: url.replace(urlParamRE, function (full, match) {
return '<%=' + _.camelCase(match) + '%>';
}),
opt: _.size(optionalVars) ? optionalVars : null,
req: _.size(requiredVars) ? requiredVars : null,
sortOrder: _.size(requiredVars) * -1
}, function (v) {
return !v;
});
});
if (urls.length > 1) {
spec.urls = _.map(_.sortBy(urls, 'sortOrder'), function (url) {
return _.omit(url, 'sortOrder');
});
} else {
spec.url = urls[0];
}
spec.params = _.transform(spec.params, function (note, param, name) {
// param.name = name;
note[name] = _.pick(param, [
'type', 'default', 'options', 'required', 'name'
]);
}, {});
if (_.size(spec.params) === 0) {
delete spec.params;
}
// escape method names with "special" keywords
var location = spec.name.split('.').join('.prototype.')
.replace(/(^|\.)(delete|default)(\.|$)/g, '[\'$2\']');
var action = {
_methods: spec.methods,
spec: _.pick(spec, [
'params',
'url',
'urls',
'needBody',
'bulkBody',
'castExists',
'castNotFound'
]),
location: location,
docUrl: def.documentation,
name: spec.name,
allParams: allParams
};
function hasMethod(/* ...methods */) {
for (var i = 0; i < arguments.length; i++) {
if (~action._methods.indexOf(arguments[i])) {
continue;
} else {
return false;
}
}
return true;
}
function methodsAre(/* ...methods */) {
return hasMethod.apply(null, arguments) && arguments.length === action._methods.length;
}
var method;
if (action._methods.length === 1) {
method = action._methods[0];
} else {
// we need to define what the default method(s) will be
if (hasMethod('DELETE', 'POST')) {
method = 'POST';
}
else if (methodsAre('DELETE')) {
method = 'DELETE';
}
else if (methodsAre('POST', 'PUT')) {
method = action.name.match(/put/i) ? 'PUT' : 'POST';
}
else if (methodsAre('GET', 'POST')) {
method = 'POST';
}
else if (methodsAre('GET', 'HEAD')) {
if (action.spec.castExists) {
method = 'HEAD';
} else {
method = 'GET';
}
}
}
if (method) {
if (method !== 'GET') {
action.spec.method = method;
}
} else {
throw new Error('unable to pick a method for ' + JSON.stringify(action, null, ' '));
}
if (action.name === 'create') {
action.proxy = 'index';
action.transformBody = 'params.op_type = \'create\';';
}
if (actions.push(action) === specCount && doneParsing) {
module.exports.emit('ready', actions);
}
});
}
/**
* un-comment to print out the default method for any action that has multiple options
*/
module.exports = new EventEmitter();
// module.exports.on('ready', function (actions) {
// var longestName = 0;
// var reports = {
// multi_methods: [],
// get_with_body: []
// };
// actions.forEach(function (action) {
// var name;
// // console.log(action);
// if (action._methods.length > 1) {
// name = action.name + ' (' + action._methods.join('/') + ')';
// longestName = Math.max(name.length, longestName);
// reports.multi_methods.push([name, action.spec.method || 'GET', action.docUrl]);
// }
// if (action._methods.length === 1 && action._methods[0] === 'GET' && action.body) {
// name = action.name + ' (' + action._methods.join('/') + ')';
// longestName = Math.max(name.length, longestName);
// reports.get_with_body.push([name, action.spec.method || 'GET', action.docUrl]);
// }
// });
// Object.keys(reports).forEach(function (key) {
// console.log('\n' + key);
// if (reports[key].length) {
// reports[key].forEach(function (line) {
// var name = line[0];
// var def = line[1];
// var docUrl = line[2];
// var spacing = (new Array(longestName - name.length + 1)).join(' ');
// console.log(name + spacing + ' [' + def + (def.length === 3 ? ' ' : '') + '] -> ' + docUrl);
// });
// } else {
// console.log('--nada--');
// }
// console.log('\n');
// });
// });

View File

@ -1,63 +0,0 @@
module.exports = {
'cluster.nodeHotThreads': [
'/_cluster/nodes/hotthreads',
'/_cluster/nodes/hot_threads',
'/_nodes/hot_threads',
'/_cluster/nodes/{node_id}/hotthreads',
'/_cluster/nodes/{node_id}/hot_threads',
'/_nodes/{node_id}/hot_threads'
],
'cluster.nodeInfo': [
'/_cluster/nodes',
'/_nodes/settings',
'/_nodes/os',
'/_nodes/process',
'/_nodes/jvm',
'/_nodes/thread_pool',
'/_nodes/network',
'/_nodes/transport',
'/_nodes/http',
'/_nodes/plugin',
'/_cluster/nodes/{node_id}',
'/_nodes/{node_id}/settings',
'/_nodes/{node_id}/os',
'/_nodes/{node_id}/process',
'/_nodes/{node_id}/jvm',
'/_nodes/{node_id}/thread_pool',
'/_nodes/{node_id}/network',
'/_nodes/{node_id}/transport',
'/_nodes/{node_id}/http',
'/_nodes/{node_id}/plugin'
],
'cluster.nodeShutdown': [
'/_cluster/nodes/_shutdown'
],
'cluster.nodeStats': [
'/_cluster/nodes/stats',
'/_nodes/stats/{metric_family}',
'/_nodes/stats/indices/{metric}/{fields}',
'/_cluster/nodes/{node_id}/stats',
'/_nodes/{node_id}/stats/{metric_family}',
'/_nodes/{node_id}/stats/indices/{metric}/{fields}'
],
'get': [
'/{index}/{type}/{id}/_source'
],
'indices.deleteMapping': [
'/{index}/{type}/_mapping'
],
'indices.stats': [
'_stats/{metric_family}',
'/_stats/indexing',
'/_stats/indexing/{indexing_types}',
'/_stats/search/{search_groups}',
'/_stats/fielddata/{fields}',
'/{index}/_stats/{metric_family}',
'/{index}/_stats/indexing',
'/{index}/_stats/search/{search_groups}',
'/{index}/_stats/fielddata/{fields}'
],
'search': [
'/_search'
]
};

View File

@ -1,59 +0,0 @@
module.exports = function (force) {
var _ = require('../../../src/lib/utils');
var fs = require('fs');
var templates = require('./templates');
var restSpecUpdated = require('../../rest_spec_updated');
var outputPath = _.joinPath(__dirname, '../../../src/lib/api.js');
var docOutputDir = _.joinPath(__dirname, '../../../docs/');
function download() {
require('./actions').on('ready', function (actions) {
var namespaces = _.filter(_.map(actions, function (action) {
if (~action.location.indexOf('.')) {
var path = action.location.split('.').slice(0, -1);
_.pull(path, 'prototype');
return path.join('.');
}
}));
// seperate the proxy actions
var groups = _.groupBy(actions, function (action) {
return action.proxy ? 'proxies' : 'normal';
});
fs.unlink(outputPath, function () {
console.log('writing', actions.length, 'api actions to', outputPath);
fs.writeFileSync(outputPath, templates.apiFile({
actions: groups.normal,
proxies: groups.proxies,
namespaces: _.unique(namespaces.sort(), true)
}));
if (!fs.existsSync(docOutputDir)) {
fs.mkdirSync(docOutputDir);
}
fs.writeFileSync(docOutputDir + '_method_list.jade', templates.apiMethodList({
actions: actions
}));
fs.writeFileSync(docOutputDir + '_methods.jade', templates.apiMethods({
actions: actions
}));
});
});
}
if (force) {
download();
} else {
restSpecUpdated(function (err, updated) {
if (err || updated) {
download();
}
});
}
};

View File

@ -1 +0,0 @@
require('./generate')(require('../_force'));

View File

@ -19,9 +19,6 @@ var argv = require('optimist')
})
.argv;
// Error.stackTraceLimit = Infinity;
// process.exit();
var es = require('../../../src/elasticsearch');
var _ = require('../../../src/lib/utils');
var async = require('async');

View File

@ -22,6 +22,7 @@ api.<%= namespace %> = function <%= className %>(transport) {
});
_.each(proxies, function (action) {%>
<%= partials.client_action_proxy(action) %><%
});
%>
%>

View File

@ -9,4 +9,4 @@ _.each(allParams, function(param, paramName) { %>
}
%><% }) %>
*/
api<%= (location[0] === '[' ? '' : '.') + location %> = ca(<%= stringify(spec, true) %>);
api<%= (location[0] === '[' ? '' : '.') + location %> = ca(<%= stringify(spec, true) %>);

View File

@ -16,4 +16,4 @@ if (typeof transformBody === 'string') { %>, {
}
}<%
}
%>);
%>);

View File

@ -1,5 +1,5 @@
var _ = require('../../../../src/lib/utils');
var _ = require('../../../src/lib/utils');
var fs = require('fs');
var path = require('path');

View File

@ -0,0 +1,46 @@
module.exports = function (done) {
/**
* Creates a JSON version of the YAML test suite that can be simply bundled for use in the browser.
*/
var jsYaml = require('js-yaml');
var fs = require('relative-fs').relativeTo(__dirname);
var async = require('async');
var path = require('path');
var tests = {}; // populated in readYamlTests
// generate the yaml tests
async.series([
readYamlTests,
writeYamlTests
], done);
function readYamlTests(done) {
var testDir = path.join(__dirname, '../../src/rest-api-spec/test/');
function readDirectories(dir) {
fs.readdirSync(dir).forEach(function (filename) {
var filePath = path.join(dir, filename);
var stat = fs.statSync(filePath);
if (stat.isDirectory()) {
readDirectories(filePath);
} else if (filename.match(/\.yaml$/)) {
var file = tests[path.relative(testDir, filePath)] = [];
jsYaml.loadAll(fs.readFileSync(filePath, 'utf8'), function (doc) {
file.push(doc);
});
}
});
}
readDirectories(testDir);
done();
}
function writeYamlTests(done) {
var testFile = require('path').resolve(__dirname, '../../test/integration/yaml_suite/yaml_tests.json');
fs.writeFileSync(testFile, JSON.stringify(tests, null, ' '), 'utf8');
console.log('wrote YAML tests as JSON to', testFile);
done();
}
};

View File

@ -1,3 +0,0 @@
Download the yaml suite's test specs, runs before each run of the suite to ensure they are up to date.
run by calling `npm run generate`. Force it to update, even if their has not been a new commit, but calling with `--force` or `-f`

View File

@ -1,44 +0,0 @@
module.exports = function (force) {
/**
* Check that the test directory exists, and is less than a day old, otherwise wipe it out
* and rebuild
*/
var fs = require('fs');
var path = require('path');
var jsYaml = require('js-yaml');
var spec = require('../../get_spec');
var restSpecUpdated = require('../../rest_spec_updated');
var testFile = path.resolve(__dirname, '../../../test/integration/yaml_suite/yaml_tests.json');
function download() {
var tests = {};
fs.unlink(testFile, function () {
spec.get('test/**/*.yaml')
.on('entry', function (entry) {
var filename = path.relative('test', entry.path);
var file = tests[filename] = [];
jsYaml.loadAll(entry.data, function (doc) {
file.push(doc);
});
})
.on('end', function () {
fs.writeFileSync(testFile, JSON.stringify(tests, null, ' '), 'utf8');
console.log('download yaml tests to', testFile);
});
});
}
if (force) {
download();
} else {
restSpecUpdated(function (err, updated) {
if (err || updated) {
download();
}
});
}
};

View File

@ -1 +0,0 @@
require('./generate')(require('../_force'));

19
scripts/jenkins.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
echo "generate the latest version of the yaml-tests"
node scripts/generate/ --no-api 2>&1 > /dev/null
echo "\n--- unit ---"
./node_modules/.bin/mocha test/unit/test_*.js \
--require should \
--reporter ../../../test/utils/jenkins-reporter.js \
2> test-output-node-unit.xml
echo "\n--- integration ---"
# run the integration tests
./node_modules/.bin/mocha test/integration/yaml_suite/index.js \
--require should \
--host localhost \
--port $es_port \
--reporter ../../../test/utils/jenkins-reporter.js \
2> test-output-node-integration.xml

View File

@ -1,16 +1,9 @@
var server = require('./server');
var child_process = require('child_process');
var _ = require('lodash');
var open = require('open');
var fs = require('fs');
var path = require('path');
var async = require('async');
var chalk = require('chalk');
var yamlTestSourceFile = path.join(__dirname, '../../test/integration/yaml_suite/index.js');
var yamlTestBundleFile = path.join(__dirname, '../../test/integration/browser_yaml_suite/yaml_tests.js');
var clientEntryFile = path.join(__dirname, '../../src/elasticsearch.js');
var browserAppNames = _.transform({
safari: {
darwin: 'Safari'
@ -79,102 +72,45 @@ if (badKeys.length) {
console.log('opening browser suite in', server.browsers);
}
server.on('tests done', function (report) {
var reports = [];
var success = true;
_.each(report, function (testSucceeded, browser) {
var msg = browser + ':' + (success ? '✔︎' : '⚑');
if (testSucceeded) {
msg = chalk.green(msg);
} else {
msg = chalk.red(msg);
success = false;
}
reports.push(' - ' + msg);
});
console.log('test completed!\n', reports.join('\n'));
process.exit(success ? 0 : 1);
});
async.series([
function (done) {
fs.exists('dist', function (yes) {
if (!argv.forceGen && yes) {
done();
return;
}
console.log('generating client with "grunt build"');
child_process.spawn('npm', ['run', 'build_clients'], {
stdio: 'inherit'
}).on('close', function (status) {
done(status && 'grunt closed with a status code of ' + status + '. aborting.');
});
server.listen(0, function () {
server.port = server.address().port;
console.log('server listening on port', server.port);
done();
});
},
function (done) {
fs.exists(yamlTestBundleFile, function (yes) {
if (!argv.forceGen && yes) {
done();
return;
}
async.eachSeries(_.clone(server.browsers), function (browser, browserDone) {
open('http://localhost:' + server.port +
'?es_hostname=' + encodeURIComponent(argv.host) +
'&es_port=' + encodeURIComponent(argv.port) +
'&browser=' + encodeURIComponent(browser), browserAppNames[browser]);
console.log('generating browser\'s yaml_tests.js bundle');
var b = require('browserify')();
b.add(yamlTestSourceFile);
var bundle = b.bundle({
external: [
'optimist'
],
ignore: [
'test/integration/yaml_suite/reporter',
clientEntryFile
]
});
var file = fs.createWriteStream(yamlTestBundleFile, {
flags: 'w',
encoding: 'utf8',
mode: 0666
});
bundle.pipe(file);
file.once('error', function (err) {
done(err);
});
bundle.once('error', function (err) {
done(err);
});
bundle.once('end', function () {
done();
});
});
server.once('browser complete', _.bind(browserDone, null, null));
}, done);
}
], function (err) {
if (err) {
console.error(err);
process.exit(1);
} else {
server.listen(0, function () {
var port = server.address().port;
console.log('server listening on port', port);
async.eachSeries(_.clone(server.browsers), function (browser, done) {
open('http://localhost:' + port +
'?es_hostname=' + encodeURIComponent(argv.host) +
'&es_port=' + encodeURIComponent(argv.port) +
'&browser=' + encodeURIComponent(browser), browserAppNames[browser]);
server.once('browser complete', function () {
done();
});
});
});
server.on('tests done', function (report) {
var reports = [];
var success = true;
_.each(report, function (testSucceeded, browser) {
var msg = browser + ':' + (success ? '✔︎' : '⚑');
if (testSucceeded) {
msg = chalk.green(msg);
} else {
msg = chalk.red(msg);
success = false;
}
reports.push(' - ' + msg);
});
console.log('test completed!\n', reports.join('\n'));
process.exit(success ? 0 : 1);
});
}
});

View File

@ -4,7 +4,7 @@ var path = require('path');
var fs = require('fs');
var _ = require('lodash');
var chalk = require('chalk');
var makeJUnitXml = require('../make_j_unit_xml');
var makeJUnitXml = require('../../test/utils/make_j_unit_xml');
var docRoot = path.join(__dirname, '../../test/integration/browser_yaml_suite');
chalk.enabled = true;
@ -21,8 +21,14 @@ var server = http.createServer(function (req, resp) {
req.filename = path.join(docRoot, req.uri);
var end = resp.end;
resp.end = function () {
console.log(chalk[this.statusCode < 300 ? 'green' : 'red'](this.statusCode), req.uri);
resp.end = function (data) {
console.log(
chalk[this.statusCode < 300 ? 'green' : 'red'](this.statusCode),
req.uri,
'-',
Buffer.byteLength(data || ''),
'bytes'
);
end.apply(resp, arguments);
};

View File

@ -1,7 +1,6 @@
var async = require('async');
var cp = require('child_process');
var chalk = require('chalk');
var path = require('path');
var argv = require('optimist')
.usage([
'Runner for the Elasticsearch.js unit and integration tests in both node and the browser.',
@ -25,7 +24,7 @@ var argv = require('optimist')
alias: 's'
},
unit: {
default: true,
default: false,
alias: 'u'
},
integration: {
@ -44,10 +43,6 @@ var argv = require('optimist')
default: '*',
alias: 'b'
},
'xml-output': {
default: true,
alias: 'x'
},
'check-upstream': {
default: false,
description: 'check for remote updates to the yaml test suite'
@ -61,22 +56,17 @@ if (process.argv.indexOf('help') + process.argv.indexOf('--help') + process.argv
if (process.env.npm_config_argv) {
// when called by NPM
argv = argv.parse(JSON.parse(process.env.npm_config_argv).original);
argv = argv.parse([].concat(process.argv).concat(JSON.parse(process.env.npm_config_argv).original));
} else {
// when called by NPM - via `npm test`
// when called directly
argv = argv.argv;
}
var commands = [];
var command;
if (argv['just-browser']) {
argv.server = false;
argv.browsers = '*';
}
if (argv['check-upstream']) {
command = ['node', 'scripts/generate/yaml_tests/index.js'];
command = ['node', 'scripts/generate'];
if (argv.force) {
command.push('--force');
}
@ -85,21 +75,20 @@ if (argv['check-upstream']) {
if (argv.unit) {
if (argv.server) {
commands.push(['mocha', 'test/unit/test_*.js', '--require should']);
commands.push(['./node_modules/.bin/mocha', 'test/unit/test_*.js', '--require should']);
}
if (argv.browsers) {
commands.push(['testling', '.']);
commands.push(['./node_modules/.bin/testling', '.']);
}
}
if (argv.integration) {
if (argv.server) {
commands.push([
'mocha',
'./node_modules/.bin/mocha',
'test/integration/yaml_suite/index.js',
'-b',
// '-b',
'--require', 'should',
argv.x ? '--reporter' : '', argv.x ? path.join(__dirname, '../test/integration/yaml_suite/reporter.js') : '',
'--host', argv.host,
'--port', argv.port
].filter(Boolean));
@ -125,6 +114,7 @@ if (commands.length) {
async.forEachSeries(commands, function (args, done) {
var command = args.shift();
console.log(chalk.gray.bold('\n\n' + '# ' + command + ' ' + args.join(' ')));
proc = cp.spawn(command, args, {
stdio: 'inherit'
});

View File

@ -29,4 +29,5 @@ angular.module('elasticsearch.client', [])
factory.ConnectionPool = require('./lib/connection_pool');
factory.Transport = require('./lib/transport');
return factory;
}]);

View File

@ -72,7 +72,6 @@ api.bulk = ca({
method: 'POST'
});
/**
* Perform a [clearScroll](http://www.elasticsearch.org/guide/reference/api/search/scroll/) request
*
@ -92,7 +91,6 @@ api.clearScroll = ca({
method: 'DELETE'
});
api.cluster = function ClusterNS(transport) {
this.transport = transport;
};
@ -108,7 +106,6 @@ api.cluster.prototype.getSettings = ca({
}
});
/**
* Perform a [cluster.health](http://elasticsearch.org/guide/reference/api/admin-cluster-health/) request
*
@ -182,7 +179,6 @@ api.cluster.prototype.health = ca({
]
});
/**
* Perform a [cluster.nodeHotThreads](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-hot-threads/) request
*
@ -228,7 +224,6 @@ api.cluster.prototype.nodeHotThreads = ca({
]
});
/**
* Perform a [cluster.nodeInfo](http://elasticsearch.org/guide/reference/api/admin-cluster-nodes-info/) request
*
@ -302,7 +297,6 @@ api.cluster.prototype.nodeInfo = ca({
]
});
/**
* Perform a [cluster.nodeShutdown](http://elasticsearch.org/guide/reference/api/admin-cluster-nodes-shutdown/) request
*
@ -336,7 +330,6 @@ api.cluster.prototype.nodeShutdown = ca({
method: 'POST'
});
/**
* Perform a [cluster.nodeStats](http://elasticsearch.org/guide/reference/api/admin-cluster-nodes-stats/) request
*
@ -412,7 +405,6 @@ api.cluster.prototype.nodeStats = ca({
]
});
/**
* Perform a [cluster.putSettings](http://elasticsearch.org/guide/reference/api/admin-cluster-update-settings/) request
*
@ -425,7 +417,6 @@ api.cluster.prototype.putSettings = ca({
method: 'PUT'
});
/**
* Perform a [cluster.reroute](http://elasticsearch.org/guide/reference/api/admin-cluster-reroute/) request
*
@ -450,7 +441,6 @@ api.cluster.prototype.reroute = ca({
method: 'POST'
});
/**
* Perform a [cluster.state](http://elasticsearch.org/guide/reference/api/admin-cluster-state/) request
*
@ -503,7 +493,6 @@ api.cluster.prototype.state = ca({
}
});
/**
* Perform a [count](http://elasticsearch.org/guide/reference/api/count/) request
*
@ -568,7 +557,6 @@ api.count = ca({
method: 'POST'
});
/**
* Perform a [delete](http://elasticsearch.org/guide/reference/api/delete/) request
*
@ -645,7 +633,6 @@ api['delete'] = ca({
method: 'DELETE'
});
/**
* Perform a [deleteByQuery](http://www.elasticsearch.org/guide/reference/api/delete-by-query/) request
*
@ -742,7 +729,6 @@ api.deleteByQuery = ca({
method: 'DELETE'
});
/**
* Perform a [exists](http://elasticsearch.org/guide/reference/api/get/) request
*
@ -796,7 +782,6 @@ api.exists = ca({
method: 'HEAD'
});
/**
* Perform a [explain](http://elasticsearch.org/guide/reference/api/explain/) request
*
@ -896,7 +881,6 @@ api.explain = ca({
method: 'POST'
});
/**
* Perform a [get](http://elasticsearch.org/guide/reference/api/get/) request
*
@ -966,7 +950,6 @@ api.get = ca({
}
});
/**
* Perform a [getSource](http://elasticsearch.org/guide/reference/api/get/) request
*
@ -1026,7 +1009,6 @@ api.getSource = ca({
}
});
/**
* Perform a [index](http://elasticsearch.org/guide/reference/api/index_/) request
*
@ -1137,7 +1119,6 @@ api.index = ca({
method: 'POST'
});
api.indices = function IndicesNS(transport) {
this.transport = transport;
};
@ -1204,7 +1185,6 @@ api.indices.prototype.analyze = ca({
method: 'POST'
});
/**
* Perform a [indices.clearCache](http://www.elasticsearch.org/guide/reference/api/admin-indices-clearcache/) request
*
@ -1283,7 +1263,6 @@ api.indices.prototype.clearCache = ca({
method: 'POST'
});
/**
* Perform a [indices.close](http://www.elasticsearch.org/guide/reference/api/admin-indices-open-close/) request
*
@ -1314,7 +1293,6 @@ api.indices.prototype.close = ca({
method: 'POST'
});
/**
* Perform a [indices.create](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index/) request
*
@ -1345,7 +1323,6 @@ api.indices.prototype.create = ca({
method: 'POST'
});
/**
* Perform a [indices.delete](http://www.elasticsearch.org/guide/reference/api/admin-indices-delete-index/) request
*
@ -1380,7 +1357,6 @@ api.indices.prototype['delete'] = ca({
method: 'DELETE'
});
/**
* Perform a [indices.deleteAlias](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -1415,7 +1391,6 @@ api.indices.prototype.deleteAlias = ca({
method: 'DELETE'
});
/**
* Perform a [indices.deleteMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-delete-mapping/) request
*
@ -1446,7 +1421,6 @@ api.indices.prototype.deleteMapping = ca({
method: 'DELETE'
});
/**
* Perform a [indices.deleteTemplate](http://www.elasticsearch.org/guide/reference/api/admin-indices-templates/) request
*
@ -1477,7 +1451,6 @@ api.indices.prototype.deleteTemplate = ca({
method: 'DELETE'
});
/**
* Perform a [indices.deleteWarmer](http://www.elasticsearch.org/guide/reference/api/admin-indices-warmers/) request
*
@ -1532,7 +1505,6 @@ api.indices.prototype.deleteWarmer = ca({
method: 'DELETE'
});
/**
* Perform a [indices.exists](http://www.elasticsearch.org/guide/reference/api/admin-indices-indices-exists/) request
*
@ -1553,7 +1525,6 @@ api.indices.prototype.exists = ca({
method: 'HEAD'
});
/**
* Perform a [indices.existsAlias](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -1599,7 +1570,6 @@ api.indices.prototype.existsAlias = ca({
method: 'HEAD'
});
/**
* Perform a [indices.existsType](http://www.elasticsearch.org/guide/reference/api/admin-indices-types-exists/) request
*
@ -1636,7 +1606,6 @@ api.indices.prototype.existsType = ca({
method: 'HEAD'
});
/**
* Perform a [indices.flush](http://www.elasticsearch.org/guide/reference/api/admin-indices-flush/) request
*
@ -1684,7 +1653,6 @@ api.indices.prototype.flush = ca({
method: 'POST'
});
/**
* Perform a [indices.getAlias](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -1728,7 +1696,6 @@ api.indices.prototype.getAlias = ca({
]
});
/**
* Perform a [indices.getAliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -1757,7 +1724,6 @@ api.indices.prototype.getAliases = ca({
]
});
/**
* Perform a [indices.getFieldMapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html) request
*
@ -1811,7 +1777,6 @@ api.indices.prototype.getFieldMapping = ca({
]
});
/**
* Perform a [indices.getMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-get-mapping/) request
*
@ -1846,7 +1811,6 @@ api.indices.prototype.getMapping = ca({
]
});
/**
* Perform a [indices.getSettings](http://www.elasticsearch.org/guide/reference/api/admin-indices-get-settings/) request
*
@ -1869,7 +1833,6 @@ api.indices.prototype.getSettings = ca({
]
});
/**
* Perform a [indices.getTemplate](http://www.elasticsearch.org/guide/reference/api/admin-indices-templates/) request
*
@ -1892,7 +1855,6 @@ api.indices.prototype.getTemplate = ca({
]
});
/**
* Perform a [indices.getWarmer](http://www.elasticsearch.org/guide/reference/api/admin-indices-warmers/) request
*
@ -1939,7 +1901,6 @@ api.indices.prototype.getWarmer = ca({
]
});
/**
* Perform a [indices.open](http://www.elasticsearch.org/guide/reference/api/admin-indices-open-close/) request
*
@ -1970,7 +1931,6 @@ api.indices.prototype.open = ca({
method: 'POST'
});
/**
* Perform a [indices.optimize](http://www.elasticsearch.org/guide/reference/api/admin-indices-optimize/) request
*
@ -2033,7 +1993,6 @@ api.indices.prototype.optimize = ca({
method: 'POST'
});
/**
* Perform a [indices.putAlias](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -2088,7 +2047,6 @@ api.indices.prototype.putAlias = ca({
method: 'PUT'
});
/**
* Perform a [indices.putMapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-put-mapping/) request
*
@ -2128,7 +2086,6 @@ api.indices.prototype.putMapping = ca({
method: 'PUT'
});
/**
* Perform a [indices.putSettings](http://www.elasticsearch.org/guide/reference/api/admin-indices-update-settings/) request
*
@ -2159,7 +2116,6 @@ api.indices.prototype.putSettings = ca({
method: 'PUT'
});
/**
* Perform a [indices.putTemplate](http://www.elasticsearch.org/guide/reference/api/admin-indices-templates/) request
*
@ -2194,7 +2150,6 @@ api.indices.prototype.putTemplate = ca({
method: 'PUT'
});
/**
* Perform a [indices.putWarmer](http://www.elasticsearch.org/guide/reference/api/admin-indices-warmers/) request
*
@ -2241,7 +2196,6 @@ api.indices.prototype.putWarmer = ca({
method: 'PUT'
});
/**
* Perform a [indices.refresh](http://www.elasticsearch.org/guide/reference/api/admin-indices-refresh/) request
*
@ -2281,7 +2235,6 @@ api.indices.prototype.refresh = ca({
method: 'POST'
});
/**
* Perform a [indices.segments](http://elasticsearch.org/guide/reference/api/admin-indices-segments/) request
*
@ -2320,7 +2273,6 @@ api.indices.prototype.segments = ca({
]
});
/**
* Perform a [indices.snapshotIndex](http://www.elasticsearch.org/guide/reference/api/admin-indices-gateway-snapshot/) request
*
@ -2356,7 +2308,6 @@ api.indices.prototype.snapshotIndex = ca({
method: 'POST'
});
/**
* Perform a [indices.stats](http://elasticsearch.org/guide/reference/api/admin-indices-stats/) request
*
@ -2474,7 +2425,6 @@ api.indices.prototype.stats = ca({
]
});
/**
* Perform a [indices.status](http://elasticsearch.org/guide/reference/api/admin-indices-status/) request
*
@ -2521,7 +2471,6 @@ api.indices.prototype.status = ca({
]
});
/**
* Perform a [indices.updateAliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases/) request
*
@ -2546,7 +2495,6 @@ api.indices.prototype.updateAliases = ca({
method: 'POST'
});
/**
* Perform a [indices.validateQuery](http://www.elasticsearch.org/guide/reference/api/validate/) request
*
@ -2610,7 +2558,6 @@ api.indices.prototype.validateQuery = ca({
method: 'POST'
});
/**
* Perform a [info](http://elasticsearch.org/guide/) request
*
@ -2622,7 +2569,6 @@ api.info = ca({
}
});
/**
* Perform a [mget](http://elasticsearch.org/guide/reference/api/multi-get/) request
*
@ -2690,7 +2636,6 @@ api.mget = ca({
method: 'POST'
});
/**
* Perform a [mlt](http://elasticsearch.org/guide/reference/api/more-like-this/) request
*
@ -2814,7 +2759,6 @@ api.mlt = ca({
method: 'POST'
});
/**
* Perform a [msearch](http://www.elasticsearch.org/guide/reference/api/multi-search/) request
*
@ -2866,7 +2810,6 @@ api.msearch = ca({
method: 'POST'
});
/**
* Perform a [percolate](http://elasticsearch.org/guide/reference/api/percolate/) request
*
@ -2897,7 +2840,6 @@ api.percolate = ca({
method: 'POST'
});
/**
* Perform a [scroll](http://www.elasticsearch.org/guide/reference/api/search/scroll/) request
*
@ -2931,7 +2873,6 @@ api.scroll = ca({
method: 'POST'
});
/**
* Perform a [search](http://www.elasticsearch.org/guide/reference/api/search/) request
*
@ -3122,7 +3063,6 @@ api.search = ca({
method: 'POST'
});
/**
* Perform a [suggest](http://elasticsearch.org/guide/reference/api/search/suggest/) request
*
@ -3170,7 +3110,6 @@ api.suggest = ca({
method: 'POST'
});
/**
* Perform a [update](http://elasticsearch.org/guide/reference/api/update/) request
*
@ -3297,5 +3236,4 @@ api.create = ca.proxy(api.index, {
transform: function (params) {
params.op_type = 'create';
}
});
});

View File

@ -24,7 +24,7 @@ AngularConnector.prototype.request = function (params, cb) {
cache: false,
timeout: abort.promise
}).then(function (response) {
cb(null, response.data, response.status);
cb(null, response.data, response.status, response.headers);
}, function (err) {
cb(new ConnectionFault(err.message));
});

View File

@ -68,6 +68,10 @@ HttpConnector.prototype.makeReqParams = function (params) {
agent: this.agent
};
if (!reqParams.path) {
reqParams.path = '/';
}
var query = this.host.query ? _.clone(this.host.query) : {};
if (params.query) {
@ -87,6 +91,7 @@ HttpConnector.prototype.request = function (params, cb) {
var request;
var response;
var status = 0;
var headers;
var log = this.log;
var reqParams = this.makeReqParams(params);
@ -116,6 +121,7 @@ HttpConnector.prototype.request = function (params, cb) {
request = this.hand.request(reqParams, function (_incoming) {
incoming = _incoming;
status = incoming.statusCode;
headers = incoming.headers;
incoming.setEncoding('utf8');
response = '';

View File

@ -55,9 +55,9 @@ XhrConnector.prototype.request = function (params, cb) {
var async = params.async === false ? false : asyncDefault;
if (params.auth) {
xhr.open(params.method, url, async, params.auth.user, params.auth.pass);
xhr.open(params.method || 'GET', url, async, params.auth.user, params.auth.pass);
} else {
xhr.open(params.method, url, async);
xhr.open(params.method || 'GET', url, async);
}
xhr.onreadystatechange = function () {

View File

@ -305,6 +305,7 @@ Log.prototype.trace = function (method, requestUrl, body, responseBody, response
function prettyJSON(body) {
try {
// TESTME
return JSON.stringify(JSON.parse(body), null, ' ').replace(/'/g, '\\\'');
} catch (e) {
return body || '';

View File

@ -35,7 +35,7 @@ function Transport(config) {
this.maxRetries = config.hasOwnProperty('maxRetries') ? config.maxRetries : 3;
// setup requestTimeout default
this.requestTimeout = config.hasOwnProperty('requestTimeout') ? config.requestTimeout : 10000;
this.requestTimeout = config.hasOwnProperty('requestTimeout') ? config.requestTimeout : 30000;
// randomizeHosts option
var randomizeHosts = config.hasOwnProperty('randomizeHosts') ? !!config.randomizeHosts : true;
@ -98,7 +98,7 @@ Transport.prototype.request = function (params, cb) {
var remainingRetries = this.maxRetries;
var connection; // set in sendReqWithConnection
var aborted = false; // several connector will respond with an error when the request is aborted
var requestAbort; // an abort function, returned by connection#request()
var requestAborter; // an abort function, returned by connection#request()
var requestTimeout; // the general timeout for the total request (inculding all retries)
var requestTimeoutId; // the id of the ^timeout
var request; // the object returned to the user, might be a promise
@ -118,7 +118,7 @@ Transport.prototype.request = function (params, cb) {
params.req = {
method: params.method,
path: params.path,
path: params.path || '/',
query: params.query,
body: params.body,
};
@ -132,18 +132,20 @@ Transport.prototype.request = function (params, cb) {
respond(err);
} else if (_connection) {
connection = _connection;
requestAbort = connection.request(params.req, checkRespForFailure);
requestAborter = connection.request(params.req, checkRespForFailure);
} else {
self.log.warning('No living connections');
respond(new errors.NoConnections());
}
}
function checkRespForFailure(err, body, status) {
function checkRespForFailure(err, body, status, headers) {
if (aborted) {
return;
}
requestAborter = void 0;
if (err) {
connection.setStatus('dead');
if (remainingRetries) {
@ -156,21 +158,26 @@ Transport.prototype.request = function (params, cb) {
}
} else {
self.log.info('Request complete');
respond(void 0, body, status);
respond(void 0, body, status, headers);
}
}
function respond(err, body, status) {
function respond(err, body, status, headers) {
if (aborted) {
return;
}
clearTimeout(requestTimeoutId);
var parsedBody;
if (!err && body) {
parsedBody = self.serializer.deserialize(body);
if (parsedBody == null) {
err = new errors.Serialization();
if (!headers || headers['content-type'] === 'application/json') {
parsedBody = self.serializer.deserialize(body);
if (parsedBody == null) {
err = new errors.Serialization();
}
} else {
parsedBody = body;
}
}
@ -222,8 +229,8 @@ Transport.prototype.request = function (params, cb) {
aborted = true;
remainingRetries = 0;
clearTimeout(requestTimeoutId);
if (typeof requestAbort === 'function') {
requestAbort();
if (typeof requestAborter === 'function') {
requestAborter();
}
}

1
src/rest-api-spec Submodule

Submodule src/rest-api-spec added at bd7f4e3c5d

File diff suppressed because it is too large Load Diff

View File

@ -11,23 +11,27 @@ var argv = require('./argv');
var testDir = path.resolve(__dirname, './tests');
var doPattern = new Minimatch(argv.match);
// before running any tests...
before(function (done) {
// start our personal ES Server
this.timeout(null);
clientManager.create(done);
});
describe('yaml -', function () {
before(function (done) {
// make sure ES is empty
clientManager.get().indices.delete({
index: '*',
ignore: 404
}, done);
});
// before running any tests...
before(function (done) {
// start our personal ES Server
this.timeout(null);
clientManager.create(done);
});
var files = _.map(require('./yaml_tests.json'), function (docs, filename) {
if (doPattern.match(filename)) {
return new YamlFile(filename, docs);
}
});
before(function (done) {
// make sure ES is empty
clientManager.get().indices.delete({
index: '*',
ignore: 404
}, done);
});
var files = _.map(require('./yaml_tests.json'), function (docs, filename) {
if (doPattern.match(filename)) {
return new YamlFile(filename, docs);
}
});
});

484
test/mocks/browser_http.js Normal file
View File

@ -0,0 +1,484 @@
/* jshint browser:true */
/* global ActiveXObject */
//////////////
/// Extended version of:
/// https://github.com/philikon/MockHttpRequest/
//////////////
/*
* Mock XMLHttpRequest (see http://www.w3.org/TR/XMLHttpRequest)
*
* Written by Philipp von Weitershausen <philipp@weitershausen.de>
* Released under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* For test interaction it exposes the following attributes:
*
* - method, url, urlParts, async, user, password
* - requestText
*
* as well as the following methods:
*
* - getRequestHeader(header)
* - setResponseHeader(header, value)
* - receive(status, data)
* - err(exception)
* - authenticate(user, password)
*
*/
module.exports = MockHttpRequest;
var _ = require('lodash');
function MockHttpRequest() {
// These are internal flags and data structures
this.error = false;
this.sent = false;
this.requestHeaders = {};
this.responseHeaders = {};
}
MockHttpRequest.prototype = {
statusReasons: {
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing',
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non-Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status',
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Moved Temporarily',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
307: 'Temporary Redirect',
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Time-out',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Request Entity Too Large',
414: 'Request-URI Too Large',
415: 'Unsupported Media Type',
416: 'Requested range not satisfiable',
417: 'Expectation Failed',
422: 'Unprocessable Entity',
423: 'Locked',
424: 'Failed Dependency',
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Time-out',
505: 'HTTP Version not supported',
507: 'Insufficient Storage'
},
/*** State ***/
UNSENT: 0,
OPENED: 1,
HEADERS_RECEIVED: 2,
LOADING: 3,
DONE: 4,
readyState: 0,
/*** Request ***/
open: function (method, url, async, user, password) {
if (typeof method !== 'string') {
throw 'INVALID_METHOD';
}
switch (method.toUpperCase()) {
case 'CONNECT':
case 'TRACE':
case 'TRACK':
throw 'SECURITY_ERR';
case 'DELETE':
case 'GET':
case 'HEAD':
case 'OPTIONS':
case 'POST':
case 'PUT':
method = method.toUpperCase();
}
this.method = method;
if (typeof url !== 'string') {
throw 'INVALID_URL';
}
this.url = url;
this.urlParts = this.parseUri(url);
if (async === undefined) {
async = true;
}
this.async = async;
this.user = user;
this.password = password;
this.readyState = this.OPENED;
this.onreadystatechange();
},
setRequestHeader: function (header, value) {
header = header.toLowerCase();
switch (header) {
case 'accept-charset':
case 'accept-encoding':
case 'connection':
case 'content-length':
case 'cookie':
case 'cookie2':
case 'content-transfer-encoding':
case 'date':
case 'expect':
case 'host':
case 'keep-alive':
case 'referer':
case 'te':
case 'trailer':
case 'transfer-encoding':
case 'upgrade':
case 'user-agent':
case 'via':
return;
}
if ((header.substr(0, 6) === 'proxy-') || (header.substr(0, 4) === 'sec-')) {
return;
}
// it's the first call on this header field
if (this.requestHeaders[header] === undefined) {
this.requestHeaders[header] = value;
}
else {
var prev = this.requestHeaders[header];
this.requestHeaders[header] = prev + ', ' + value;
}
},
send: function (data) {
if ((this.readyState !== this.OPENED) || this.sent) {
throw 'INVALID_STATE_ERR';
}
if ((this.method === 'GET') || (this.method === 'HEAD')) {
data = null;
}
//TODO set Content-Type header?
this.error = false;
this.sent = true;
this.onreadystatechange();
// fake send
this.requestText = data;
this.onsend();
},
abort: function () {
this.responseText = null;
this.error = true;
for (var header in this.requestHeaders) {
if (this.requestHeaders.hasOwnProperty(header)) {
delete this.requestHeaders[header];
}
}
delete this.requestText;
this.onreadystatechange();
this.onabort();
this.readyState = this.UNSENT;
},
/*** Response ***/
status: 0,
statusText: '',
getResponseHeader: function (header) {
if ((this.readyState === this.UNSENT) || (this.readyState === this.OPENED) || this.error) {
return null;
}
return this.responseHeaders[header.toLowerCase()];
},
getAllResponseHeaders: function () {
var r = '';
_.each(this.responseHeaders, function (header) {
if ((header === 'set-cookie') || (header === 'set-cookie2')) {
return;
}
//TODO title case header
r += header + ': ' + this.responseHeaders[header] + '\r\n';
}, this);
return r;
},
responseText: '',
responseXML: undefined, //TODO
/*** See http://www.w3.org/TR/progress-events/ ***/
onload: function () {
// Instances should override this.
},
onprogress: function () {
// Instances should override this.
},
onerror: function () {
// Instances should override this.
},
onabort: function () {
// Instances should override this.
},
onreadystatechange: function () {
// Instances should override this.
},
/*** Properties and methods for test interaction ***/
onsend: function () {
// Instances should override this.
},
getRequestHeader: function (header) {
return this.requestHeaders[header.toLowerCase()];
},
setResponseHeader: function (header, value) {
this.responseHeaders[header.toLowerCase()] = value;
},
makeXMLResponse: function (data) {
var xmlDoc;
// according to specs from point 3.7.5:
// '1. If the response entity body is null terminate these steps
// and return null.
// 2. If final MIME type is not null, text/xml, application/xml,
// and does not end in +xml terminate these steps and return null.
var mimetype = this.getResponseHeader('Content-Type');
mimetype = mimetype && mimetype.split(';', 1)[0];
if ((mimetype == null) || (mimetype === 'text/xml') ||
(mimetype === 'application/xml') ||
(mimetype && mimetype.substring(mimetype.length - 4) === '+xml')) {
// Attempt to produce an xml response
// and it will fail if not a good xml
try {
if (window.DOMParser) {
var parser = new DOMParser();
xmlDoc = parser.parseFromString(data, 'text/xml');
} else { // Internet Explorer
xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async = 'false';
xmlDoc.loadXML(data);
}
} catch (e) {
// according to specs from point 3.7.5:
// '3. Let document be a cookie-free Document object that
// represents the result of parsing the response entity body
// into a document tree following the rules from the XML
// specifications. If this fails (unsupported character
// encoding, namespace well-formedness error etc.), terminate
// these steps return null.'
xmlDoc = null;
}
// parse errors also yield a null.
if ((xmlDoc && xmlDoc.parseError && xmlDoc.parseError.errorCode !== 0) || (xmlDoc && xmlDoc.documentElement &&
xmlDoc.documentElement.nodeName !== 'parsererror') || (xmlDoc && xmlDoc.documentElement && xmlDoc.documentElement
.nodeName !== 'html' && xmlDoc.documentElement.firstChild && xmlDoc.documentElement.firstChild.nodeName ===
'body' && xmlDoc.documentElement.firstChild.firstChild && xmlDoc.documentElement.firstChild.firstChild.nodeName
=== 'parsererror')) {
xmlDoc = null;
}
} else {
// mimetype is specified, but not xml-ish
xmlDoc = null;
}
return xmlDoc;
},
// Call this to simulate a server response
receive: function (status, data) {
if ((this.readyState !== this.OPENED) || (!this.sent)) {
// Can't respond to unopened request.
throw 'INVALID_STATE_ERR';
}
this.status = status;
this.statusText = status + ' ' + this.statusReasons[status];
this.readyState = this.HEADERS_RECEIVED;
this.onprogress();
this.onreadystatechange();
this.responseText = data;
this.responseXML = this.makeXMLResponse(data);
this.readyState = this.LOADING;
this.onprogress();
this.onreadystatechange();
this.readyState = this.DONE;
this.onreadystatechange();
this.onprogress();
this.onload();
},
// Call this to simulate a request error (e.g. NETWORK_ERR)
err: function (exception) {
if ((this.readyState !== this.OPENED) || (!this.sent)) {
// Can't respond to unopened request.
throw 'INVALID_STATE_ERR';
}
this.responseText = null;
this.error = true;
_.each(this.requestHeaders, function (header) {
delete this.requestHeaders[header];
}, this);
this.readyState = this.DONE;
if (!this.async) {
throw exception;
}
this.onreadystatechange();
this.onerror();
},
// Convenience method to verify HTTP credentials
authenticate: function (user, password) {
if (this.user) {
return (user === this.user) && (password === this.password);
}
if (this.urlParts.user) {
return ((user === this.urlParts.user) && (password === this.urlParts.password));
}
// Basic auth. Requires existence of the 'atob' function.
var auth = this.getRequestHeader('Authorization');
if (auth === undefined) {
return false;
}
if (auth.substr(0, 6) !== 'Basic ') {
return false;
}
if (typeof atob !== 'function') {
return false;
}
auth = atob(auth.substr(6));
var pieces = auth.split(':');
var requser = pieces.shift();
var reqpass = pieces.join(':');
return (user === requser) && (password === reqpass);
},
// Parse RFC 3986 compliant URIs.
// Based on parseUri by Steven Levithan <stevenlevithan.com>
// See http://blog.stevenlevithan.com/archives/parseuri
parseUri: function (str) {
/* jshint maxlen:false */
var pattern =
/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/;
var key = ['source', 'protocol', 'authority', 'userInfo', 'user',
'password', 'host', 'port', 'relative', 'path',
'directory', 'file', 'query', 'anchor'
];
var querypattern = /(?:^|&)([^&=]*)=?([^&]*)/g;
var match = pattern.exec(str);
var uri = {};
var i = 14;
while (i--) {
uri[key[i]] = match[i] || '';
}
uri.queryKey = {};
uri[key[12]].replace(querypattern, function ($0, $1, $2) {
if ($1) {
uri.queryKey[$1] = $2;
}
});
return uri;
}
};
/*
* A small mock 'server' that intercepts XMLHttpRequest calls and
* diverts them to your handler.
*
* Usage:
*
* 1. Initialize with either
* var server = new MockHttpServer(your_request_handler);
* or
* var server = new MockHttpServer();
* server.handle = function (request) { ... };
*
* 2. Call server.start() to start intercepting all XMLHttpRequests.
*
* 3. Do your tests.
*
* 4. Call server.stop() to tear down.
*
* 5. Profit!
*/
function MockHttpServer(handler) {
if (handler) {
this.handle = handler;
}
}
MockHttpRequest.MockHttpServer = MockHttpServer;
MockHttpServer.prototype = {
start: function () {
var self = this;
function Request() {
var req = this;
req.onsend = function () {
req.sendStack = (new Error()).stack;
process.nextTick(function () {
self.handle(req);
});
};
MockHttpRequest.apply(this, arguments);
}
Request.prototype = MockHttpRequest.prototype;
window.OriginalHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest = Request;
},
stop: function () {
window.XMLHttpRequest = window.OriginalHttpRequest;
},
handle: function (request) {
// Instances should override this.
}
};

View File

@ -0,0 +1,83 @@
var interceptors = [];
var complete = [];
var MockHttpRequest = require('./browser_http');
var XhrServer = MockHttpRequest.MockHttpServer;
var parseUrl = MockHttpRequest.prototype.parseUri;
var _ = require('lodash');
var server = new XhrServer(function (request) {
var reqDetails = {
method: request.method,
host: request.urlParts.host,
path: request.urlParts.relative
};
var response = _.find(interceptors, reqDetails);
if (response) {
// remove of tick down the times
if (response.times === 1) {
var i = interceptors.indexOf(response);
complete.push(interceptors.splice(i, 1));
} else {
response.times--;
}
request.receive(response.status, response.body || void 0);
} else {
throw new Error('No known match for request: ' + JSON.stringify(reqDetails));
}
});
server.start();
var mockNock = module.exports = function (url) {
var parsedUrl = parseUrl(url);
var req = {
method: 'GET',
host: parsedUrl.host,
times: 1
};
var modifyReq = {
get: function (path) {
req.path = path;
req.method = 'GET';
return modifyReq;
},
port: function (path) {
req.path = path;
req.method = 'POST';
return modifyReq;
},
times: function (times) {
req.times = times;
return modifyReq;
},
reply: function (status, body) {
req.status = status;
req.body = body;
switch (typeof req.body) {
case 'string':
case 'number':
break;
default:
try {
req.body = req.body && JSON.stringify(req.body);
} catch (e) {
req.body = req.body;
}
}
interceptors.push(req);
return mockNock(url);
},
done: mockNock.done
};
return modifyReq;
};
mockNock.done = function () {
if (interceptors.length) {
throw new Error('Some interceptors were not called: ' + JSON.stringify(interceptors));
}
};

3
test/mocks/server.js Normal file
View File

@ -0,0 +1,3 @@
var nock = require('nock');
nock.disableNetConnect();
module.exports = nock;

View File

@ -14,9 +14,7 @@ exports.make = function () {
};
stubber.autoRelease = function (item) {
if (item.restore && !~log.indexOf(item)) {
log.push(item);
}
log.push(item);
};
return stubber;

View File

@ -96,34 +96,31 @@ describe('Connection Abstract', function () {
var conn = new ConnectionAbstract(host);
conn.setStatus('alive');
conn.ping = function () {
stub(conn, 'ping', function () {
throw new Error('ping should not have been called');
};
});
conn.resuscitate();
});
it('should ping the connection after the deadTimeout, and set the status to "alive" on pong', function (done) {
var conn = new ConnectionAbstract(host);
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
stub.autoRelease(clock);
var clock;
stub.autoRelease(clock = sinon.useFakeTimers('setTimeout', 'clearTimeout'));
// schedules the resuscitate
conn.setStatus('dead');
// override the ping method to just callback without an error
conn.ping = function (cb) {
process.nextTick(function () {
cb();
});
};
stub(conn, 'ping', function (cb) {
cb();
});
// will be called after the ping calls back
conn.setStatus = function (status) {
stub(conn, 'setStatus', function (status) {
status.should.eql('alive');
clock.restore();
done();
};
});
// fast forward the clock
clock.tick(conn.deadTimeout);
@ -131,24 +128,22 @@ describe('Connection Abstract', function () {
it('should ping the connection after the deadTimeout, and set the status to "dead" on error', function (done) {
var conn = new ConnectionAbstract(host);
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
stub.autoRelease(clock);
var clock;
stub.autoRelease(clock = sinon.useFakeTimers('setTimeout', 'clearTimeout'));
// schedules the resuscitate
conn.setStatus('dead');
// override the ping method to just callback without an error
conn.ping = function (cb) {
process.nextTick(function () {
cb(new Error('server still down'));
});
};
stub(conn, 'ping', function (cb) {
cb(new Error('server still down'));
});
// will be called after the ping calls back
conn.setStatus = function (status) {
stub(conn, 'setStatus', function (status) {
status.should.eql('dead');
done();
};
});
// fast forward the clock
clock.tick(conn.deadTimeout);

View File

@ -151,12 +151,13 @@ describe('Connection Pool', function () {
};
pool.select(function (err, selection) {
should(err).Error;
should(err).be.an.instanceOf(Error);
done();
});
});
it('should automatically select the first dead connection when there no living connections', function (done) {
pool.setHosts([]);
pool._conns.alive = [];
pool._conns.dead = [1, 2, 3];
@ -188,6 +189,10 @@ describe('Connection Pool', function () {
pool._conns.dead.should.have.length(0);
});
afterEach(function () {
pool.close();
});
it('moves an alive connection to dead', function () {
connection.setStatus('dead');
@ -207,7 +212,7 @@ describe('Connection Pool', function () {
pool._conns.dead[0].should.be.exactly(connection2);
});
it('moves a does nothing when a connection is re-alive', function () {
it('does nothing when a connection is re-alive', function () {
var last = pool._conns.alive[pool._conns.alive.length - 1];
var first = pool._conns.alive[0];

View File

@ -28,7 +28,7 @@ describe('Console Logger', function () {
it('checks before using unique logging functions, falls back to #log()', function () {
var _warning = console.warn;
console.warn = null;
stub(console, 'log');
sinon.stub(console, 'log');
var logger = makeLogger();
@ -36,6 +36,7 @@ describe('Console Logger', function () {
console.log.callCount.should.eql(1);
console.warn = _warning;
console.log.restore();
});
});

View File

@ -116,7 +116,7 @@ describe('Http Connector', function () {
});
reqParams.should.include({
path: '?user_id=123&jvm=yes'
path: '/?user_id=123&jvm=yes'
});
});
@ -353,7 +353,7 @@ describe('Http Connector', function () {
it('uses TCP no delay', function (done) {
var con = new HttpConnection(new Host('localhost'));
stub(http.ClientRequest.prototype, 'setNoDelay');
var server = nock('http://localhost').get('').reply(200);
var server = nock('http://localhost').get('/').reply(200);
con.request({}, function (err, resp, status) {
http.ClientRequest.prototype.setNoDelay.callCount.should.eql(1);
@ -366,7 +366,7 @@ describe('Http Connector', function () {
it('sets the Content-Length header properly', function (done) {
var con = new HttpConnection(new Host('localhost'));
stub(http.ClientRequest.prototype, 'setHeader');
var server = nock('http://localhost').get('').reply(200);
var server = nock('http://localhost').get('/').reply(200);
var body = 'pasta and 𝄞';
body.length.should.eql(12); // nope

View File

@ -3,7 +3,6 @@ var StreamLogger = require('../../src/lib/loggers/stream');
var MockWritableStream = require('../mocks/writable_stream');
var MockOldWritableStream = require('../mocks/old_writable_stream');
var once = require('events').EventEmitter.prototype.once;
var sinon = require('sinon');
var stream = new MockWritableStream();
var _ = require('lodash');
var util = require('util');

View File

@ -4,14 +4,12 @@ var errors = require('../../src/lib/errors');
var when = require('when');
var sinon = require('sinon');
var nock = require('nock');
var nock = require('../mocks/server.js');
var should = require('should');
var _ = require('lodash');
var nodeList = require('../fixtures/short_node_list.json');
var stub = require('./auto_release_stub').make();
nock.disableNetConnect();
/**
* Allows the tests call #request() without it doing anything past trying to select
* a connection.
@ -19,10 +17,7 @@ nock.disableNetConnect();
*/
function shortCircuitRequest(tran, delay) {
stub(tran.connectionPool, 'select', function (cb) {
setTimeout(function () {
// call back with no error, and no connection === "NoConnections"
cb();
}, delay);
setTimeout(cb, delay);
});
}
@ -315,14 +310,13 @@ describe('Transport Class', function () {
done();
});
});
it('logs when it begins', function (done) {
it('rejects get requests with bodies', function (done) {
var trans = new Transport();
stub(trans.log, 'debug');
stub(trans.connectionPool, 'select', function (cb) {
// simulate "no connections"
process.nextTick(cb);
});
trans.request({
body: 'JSON!!',
method: 'GET'
@ -754,71 +748,90 @@ describe('Transport Class', function () {
});
describe('timeout', function () {
it('uses 10 seconds for the default', function (done) {
this.timeout(5);
var clock;
stub.autoRelease(clock = sinon.useFakeTimers('setTimeout'));
it('uses 30 seconds for the default', function () {
var clock = sinon.useFakeTimers();
var tran = new Transport({});
var err;
stub(tran.connectionPool, 'select', function (cb) {
setTimeout(cb, 11000);
tran.request({});
_.size(clock.timeouts).should.eql(1);
_.each(clock.timeouts, function (timer, id) {
timer.callAt.should.eql(30000);
clearTimeout(id);
});
tran.request({}, function (err) {
err.should.be.an.instanceOf(errors.RequestTimeout);
done();
});
clock.tick(10010);
clock.restore();
});
it('inherits the requestTimeout from the transport', function (done) {
this.timeout(5);
var clock;
stub.autoRelease(clock = sinon.useFakeTimers('setTimeout'));
it('inherits the requestTimeout from the transport', function () {
var clock = sinon.useFakeTimers();
var tran = new Transport({
requestTimeout: 5000
});
var err;
stub(tran.connectionPool, 'select', function (cb) {
setTimeout(cb, 11000);
tran.request({});
_.size(clock.timeouts).should.eql(1);
_.each(clock.timeouts, function (timer, id) {
timer.callAt.should.eql(5000);
clearTimeout(id);
});
clock.restore();
});
it('clears the timeout when the request is complete', function () {
var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
var tran = new Transport({
host: 'http://localhost:9200'
});
tran.request({}, function (err) {
var server = nock('http://localhost:9200')
.get('/')
.reply(200, {
i: 'am here'
});
tran.request({}, function (err, resp, status) {
should.not.exist(err);
resp.should.eql({ i: 'am here' });
status.should.eql(200);
Object.keys(clock.timeouts).should.have.length(0);
clock.restore();
});
});
it('timeout responds with a requestTimeout error', function (done) {
// var clock = sinon.useFakeTimers('setTimeout', 'clearTimeout');
var tran = new Transport({
host: 'http://localhost:9200'
});
var server = nock('http://localhost:9200')
.get('/')
.delay(1000)
.reply(200, {
i: 'am here'
});
tran.request({
requestTimeout: 25
}, function (err, resp, status) {
err.should.be.an.instanceOf(errors.RequestTimeout);
// Object.keys(clock.timeouts).should.have.length(0);
// clock.restore();
done();
});
clock.tick(6000);
});
[false, 0, null].forEach(function (falsy) {
it('skips the timeout when it is ' + falsy, function (done) {
this.timeout(5);
var clock;
stub.autoRelease(clock = sinon.useFakeTimers('setTimeout'));
var tran = new Transport({
requestTimeout: 5000
});
stub(tran.connectionPool, 'select', function (cb) {
setTimeout(function () {
cb(new Error('it works'));
}, 10000);
});
it('skips the timeout when it is ' + falsy, function () {
var clock = sinon.useFakeTimers();
var tran = new Transport({});
stub(tran.connectionPool, 'select', function () {});
tran.request({
requestTimeout: falsy
}, function (err) {
err.message.should.eql('it works');
done();
});
}, function (_err) {});
clock.tick(6000);
process.nextTick(function () {
clock.tick(6000);
});
_.size(clock.timeouts).should.eql(0);
clock.restore();
});
});
});

View File

@ -4,20 +4,33 @@
* @param {Runner} runner
* @api public
*/
module.exports = EsjsReporter;
module.exports = JenkinsReporter;
var Base = require('mocha/lib/reporters/base');
var _ = require('lodash');
var chalk = require('chalk');
var clientManager = require('./client_manager');
var makeJUnitXml = require('../../../scripts/make_j_unit_xml');
var makeJUnitXml = require('./make_j_unit_xml');
var fs = require('fs');
var path = require('path');
var inspect = require('util').inspect;
function EsjsReporter(runner) {
var log = (function () {
var locked = _.bind(process.stdout.write, process.stdout);
return function (str) {
if (typeof str !== 'string') {
str = inspect(str);
}
locked(str);
};
})();
function JenkinsReporter(runner) {
Base.call(this, runner);
clientManager.reporter = this;
var stats = this.stats;
var pass = 0;
var pending = 0;
var fail = 0;
var rootSuite = {
results: [],
suites: []
@ -36,7 +49,7 @@ function EsjsReporter(runner) {
// suite
suite = {
name: suite.title,
name: suite.fullTitle(),
results: [],
start: Date.now(),
stdout: '',
@ -68,9 +81,17 @@ function EsjsReporter(runner) {
});
runner.on('test end', function (test) {
// test
var color = chalk[test.state === 'passed' ? 'green' : 'red'];
log(color('.'));
if (test.state === 'passed') {
pass++;
log(chalk.green('.'));
} else if (test.pending) {
pending++;
log(chalk.grey('.'));
return;
} else {
fail++;
log(chalk.red('x'));
}
var errMsg = void 0;
@ -98,22 +119,19 @@ function EsjsReporter(runner) {
}).join('\n'));
}
if (!test.pending) {
if (stack[0]) {
stack[0].results.push({
name: test.title,
time: test.duration,
pass: test.state === 'passed',
test: test
});
}
if (stack[0]) {
stack[0].results.push({
name: test.title,
time: test.duration,
pass: test.state === 'passed',
test: test
});
}
});
runner.on('end', function () {
restoreStdio();
var outputFilename = path.join(__dirname, '../../../test-output-node-yaml.xml');
var xml = makeJUnitXml('node ' + process.version + ' yaml tests', {
var xml = makeJUnitXml('node ' + process.version, {
stats: stats,
suites: _.map(rootSuite.suites, function removeElements(suite) {
var s = {
@ -131,16 +149,15 @@ function EsjsReporter(runner) {
return s;
})
});
fs.writeFileSync(outputFilename, xml);
console.log('\nwrote log to', outputFilename);
});
console.error(xml);
var log = (function () {
var locked = _.bind(process.stdout.write, process.stdout);
return function (str) {
locked(str);
};
})();
console.log('\n' + [
'tests complete in ' + (Math.round(stats.duration / 10) / 100) + ' seconds',
' fail: ' + chalk.red(stats.failures),
' pass: ' + chalk.green(stats.passes),
' pending: ' + chalk.grey(stats.pending)
].join('\n'));
});
// overload the write methods on stdout and stderr
['stdout', 'stderr'].forEach(function (name) {

View File

@ -32,8 +32,9 @@ var _ = require('lodash');
function makeJUnitXml(runnerName, testDetails) {
_.each(testDetails.suites, function serializeSuite(suiteInfo) {
var suite = suites.ele('testsuite', {
package: 'elasticsearch-js:yaml_tests',
package: 'elasticsearch-js',
id: suiteCount++,
name: suiteInfo.name,
timestamp: moment(suiteInfo.start).toJSON(),
@ -45,13 +46,16 @@ function makeJUnitXml(runnerName, testDetails) {
});
_.each(suiteInfo.results, function (testInfo) {
var section;
var parts = suiteInfo.name.replace(/\.yaml$/, '').replace(/\./g, '_').split(/\//);
var section = parts.shift();
var behavior = parts.join('/');
if (suiteInfo.name.match(/\/.*\.yaml$/)) {
section = suiteInfo.name.split('/').slice(0, -1).join('/');
} else {
section = suiteInfo.name;
}
var testcase = suite.ele('testcase', {
name: behavior + ' - ' + testInfo.name,
name: testInfo.name,
time: (testInfo.time || 0) / 1000,
classname: runnerName + '.' + section
});
@ -73,8 +77,12 @@ function makeJUnitXml(runnerName, testDetails) {
_.each(suiteInfo.suites, serializeSuite);
}
suite.ele('system-out', {}).cdata(suiteInfo.stdout);
suite.ele('system-err', {}).cdata(suiteInfo.stderr);
// if (suiteInfo.stdout.trim()) {
// suite.ele('system-out', {}).cdata(suiteInfo.stdout);
// }
// if (suiteInfo.stderr.trim()) {
// suite.ele('system-err', {}).cdata(suiteInfo.stderr);
// }
});
return suites.toString({ pretty: true});