diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..7c74cdd66 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,74 @@ +'use strict'; + +module.exports = function (grunt) { + + var pre = [ + 'src/pre.js', + 'src/shared.js', + 'src/serializer.js', + 'src/serializer/*', + 'src/selector.js', + 'src/logger/*', + 'src/transport.js' + ]; + + var post = ['src/client.js','src/post.js']; + + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + meta: { + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' + + ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= pkg.license %> */\n\n' + }, + concat: { + options: { + banner: '<%= meta.banner %>' + }, + node: { + src: pre.concat(['src/transport/elasticsearch-node.js'],post), + dest: 'dist/elasticsearch-node.js' + } + }, + nodeunit: { + files: ['test/**/*.js'] + }, + jshint: { + files: ['Gruntfile.js', '<%= concat.node.dest %>', 'test/**/*.js' ], + options: { + bitwise: true, + curly: true, + eqeqeq: true, + immed: true, + indent: 2, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + globalstrict: true, + devel: true, + node: true, + globals: { + exports: true, + module: false + } + } + } + }); + + // load plugins + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-nodeunit'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + + + // Default task. + grunt.registerTask('default', ['concat','nodeunit','jshint']); + +}; \ No newline at end of file diff --git a/dist/elasticsearch-node.js b/dist/elasticsearch-node.js new file mode 100644 index 000000000..52265bdf4 --- /dev/null +++ b/dist/elasticsearch-node.js @@ -0,0 +1,415 @@ +/*! elasticsearch-js - v0.0.1a - 2013-06-21 + * Copyright (c) 2013 Rashid Khan; Licensed Apache License */ + +(function() { + + 'use strict'; + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // save the previous version of ejs + var _esj = root && root.esj, + esj; + + // Create the esj object + if (typeof exports !== 'undefined') { + esj = exports; + } else { + esj = root.ejs = {}; + } + // (c) 2013 Rashid Khan, Elasticsearch BV + // Portions of this file are borrowed from Underscore js, + + // Set the aliases that underscore uses + var + slice = Array.prototype.slice, + toString = Object.prototype.toString, + hasOwnProp = Object.prototype.hasOwnProperty, + nativeForEach = Array.prototype.forEach, + nativeIsArray = Array.isArray, + nativeIndexOf = Array.prototype.indexOf, + has, + each, + defaults, + extend, + indexOf, + isUndefined, + shuffle, + queryString, + breaker = {}; + + // Has own property? + has = function (obj, key) { + return hasOwnProp.call(obj, key); + }; + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + each = function (obj, iterator, context) { + if (obj == null) { + return; + } + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) { + return; + } + } + } else { + for (var key in obj) { + if (has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) { + return; + } + } + } + } + }; + + // Fill in a given object with default properties. + defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Extend a given object with all the properties in passed-in object(s). + extend = function (obj) { + each(slice.call(arguments, 1), function (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Returns the index at which value can be found in the array, or -1 if + // value is not present in the array. + indexOf = function (array, item) { + if (array == null) { + return -1; + } + + var i = 0, l = array.length; + if (nativeIndexOf && array.indexOf === nativeIndexOf) { + return array.indexOf(item); + } + + for (; i < l; i++) { + if (array[i] === item) { + return i; + + } + } + + return -1; + }; + + // Is an object undefined? + isUndefined = function(obj) { + return obj === void 0; + }; + + // shuffle an array + shuffle = function(obj) { + var shuffled = [], rand; + each(obj, function(value, index, list) { + if (index === 0) { + shuffled[0] = value; + } else { + rand = Math.floor(Math.random() * (index + 1)); + shuffled[index] = shuffled[rand]; + shuffled[rand] = value; + } + }); + return shuffled; + }; + + // Takes an object and makes it into a query string + queryString = function(obj) { + var str = []; + each(obj,function(v,k){ + str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); + }); + return str.join("&"); + }; + /* Generic serializer, does nothing */ + + esj.Serializer = {}; + /* JSON serializer */ + + esj.Serializer.json = function() {}; + + esj.Serializer.json.prototype = (function() { + + return { + dump : function(obj) { + return JSON.stringify(obj); + }, + + load : function(string) { + return JSON.parse(string); + } + }; + + } ()); + /* Host selectors */ + + esj.Selector = { + roundRobin : function(hosts) { + hosts.unshift(hosts.pop()); + return hosts[0]; + }, + random : function(hosts) { + hosts = shuffle(hosts); + return hosts[0]; + } + }; + + + + /** + * + * esj.Log is a basic logger with error, warn, info and debug levels + * + * @typedef {Object} Log + * @param {Transport} transport the transport ob + * + * @property {Object} options The options passed and merged with defaults + * @property {Transport} transport The esj.Transport for this Log + * + */ + esj.Log = function(transport,options) { + options = options || {}; + var _d = { + error : true, + warn : true, + info : false, + debug : false + }; + + this.options = defaults(options,_d); + this.transport = transport; + }; + + esj.Log.prototype = (function() { + + var error = function(m) { + if (this.options.error) { + console.error(m); + return m; + } else { + return false; + } + }; + + var warn = function(m) { + if (this.options.warn){ + console.warn(m); + return m; + } else { + return false; + } + }; + + var info = function(m) { + if (this.options.info){ + console.info(m); + return m; + } else { + return false; + } + }; + + var debug = function(m) { + if (this.options.debug){ + console.log(m); + return m; + } else { + return false; + } + }; + + return { + error : error, + warn : warn, + info : info, + debug : debug + }; + + } ()); + esj.Trace = function(transport,options) { + options = options || {}; + var _d = { + info : false, + trace : false + }; + + this.options = defaults(options,_d); + this.transport = transport; + }; + + // TODO: Make this properly format the messages. Implement helper methods + esj.Trace.prototype = (function() { + + var info = function(msg) { + if (this.options.info){ + console.info(this.transport.options.hosts+" "+msg); + return msg; + } else { + return false; + } + }; + + var trace = function(msg) { + if (this.options.debug) { + console.log(this.transport.options.hosts+" "+msg); + return msg; + } else { + return false; + } + }; + + return { + info : info, + trace : trace + }; + + } ()); + + + esj.Transport = function (options) { + + options = options || {}; + + var _d = { + hosts : ['localhost:9200'], + //nodes_to_host_callback : construct_hosts_list, + sniff_on_start : false, + sniff_after_requests : 0, + sniff_on_connection_fail : false, + max_retries : 3, + selector : esj.Selector.roundRobin + }; + + // These are all unique to each instance of client + this.options = defaults(options,_d); + + // For conviences + this.selector = this.options.selector; + + }; + /* elasticsearch-js nodejs transport */ + + var http = require('http'); + + esj.Transport.prototype = (function() { + + // Split hostname:port into its repective parts + var splitHost = function(u) { + var s = u.split(':'); + return {host:s[0],port:s[1]}; + }; + + // Meta function for handling any http request that can have a body (PUT,POST,DELETE) + var performRequest = function (context,method, path, params, body, successcb, errorcb, retries) { + + var + //context = context, + host = splitHost(context.selector(context.options.hosts)), + options = { + host: host.host, + port: host.port, + path: path + '?' + queryString(params), + method: method, + headers: { + 'Content-Type': 'application/json' + } + }; + var request = http.request(options, function (res) { + + var data = ''; + res.setEncoding('utf8'); + + res.on('data', function (d) { + data = data + d; + }); + + res.on('end', function () { + + var response = { + data : data.charAt(0) === '{' ? JSON.parse(data) : data, + headers : res.headers, + status : res.statusCode + }; + + if (successcb != null && response.status < 300) { + successcb(response); + } else if (errorcb != null) { + errorcb(response); + } + }); + + }); + + if (errorcb != null) { + request.on('error', errorcb); + } + + if(method !== 'GET' && method !== 'HEAD') { + request.write(body); + } + + request.end(); + }; + + // Aliases to performRequest + var put = function (path, params, body, successcb, errorcb) {performRequest(this, 'PUT', path, params, body, successcb, errorcb);}; + var post = function (path, params, body, successcb, errorcb) {performRequest(this, 'POST', path, params, body, successcb, errorcb);}; + var del = function (path, params, body, successcb, errorcb) {performRequest(this, 'DELETE', path, params, body, successcb, errorcb);}; + var get = function (path, params, body, successcb, errorcb) {performRequest(this, 'GET', path, params, body, successcb, errorcb);}; + var head = function (path, params, body, successcb, errorcb) {performRequest(this, 'GET', path, params, body, successcb, errorcb);}; + + // Public functions + return { + get : get, + put : put, + post : post, + del : del, + head : head + }; + + } ()); + + /* + + */ + + // Expose the client object + esj.Client = function(options) { + this.options = options || {}; + + // For convience + this.transport = this.options.transport || new esj.Transport(this.options); + this.logger = this.options.logger || new esj.Log(this.transport); + this.tracer = this.options.tracer || new esj.Trace(this.transport); + this.serializer = this.options.serializer || new esj.Serializer.json(); + + }; + + + + +}).call(this); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..0b722acc2 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "author": { + "name": "Rashid Khan", + "company": "Elasticsearch BV" + }, + "name": "elasticsearch-js", + "version": "0.0.1a", + "devDependencies": { + "grunt": "~0.4.0", + "grunt-contrib": "~0.7.0", + "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-jshint": "~0.6.0", + "grunt-contrib-nodeunit": "~0.2.0" + }, + "license": "Apache License" +} diff --git a/src/client.js b/src/client.js new file mode 100644 index 000000000..4021a5681 --- /dev/null +++ b/src/client.js @@ -0,0 +1,16 @@ + /* + + */ + + // Expose the client object + esj.Client = function(options) { + this.options = options || {}; + + // For convience + this.transport = this.options.transport || new esj.Transport(this.options); + this.logger = this.options.logger || new esj.Log(this.transport); + this.tracer = this.options.tracer || new esj.Trace(this.transport); + this.serializer = this.options.serializer || new esj.Serializer.json(); + + }; + diff --git a/src/logger/log.js b/src/logger/log.js new file mode 100644 index 000000000..06047aab3 --- /dev/null +++ b/src/logger/log.js @@ -0,0 +1,72 @@ + + + /** + * + * esj.Log is a basic logger with error, warn, info and debug levels + * + * @typedef {Object} Log + * @param {Transport} transport the transport ob + * + * @property {Object} options The options passed and merged with defaults + * @property {Transport} transport The esj.Transport for this Log + * + */ + esj.Log = function(transport,options) { + options = options || {}; + var _d = { + error : true, + warn : true, + info : false, + debug : false + }; + + this.options = defaults(options,_d); + this.transport = transport; + }; + + esj.Log.prototype = (function() { + + var error = function(m) { + if (this.options.error) { + console.error(m); + return m; + } else { + return false; + } + }; + + var warn = function(m) { + if (this.options.warn){ + console.warn(m); + return m; + } else { + return false; + } + }; + + var info = function(m) { + if (this.options.info){ + console.info(m); + return m; + } else { + return false; + } + }; + + var debug = function(m) { + if (this.options.debug){ + console.log(m); + return m; + } else { + return false; + } + }; + + return { + error : error, + warn : warn, + info : info, + debug : debug + }; + + } ()); \ No newline at end of file diff --git a/src/logger/trace.js b/src/logger/trace.js new file mode 100644 index 000000000..17643483b --- /dev/null +++ b/src/logger/trace.js @@ -0,0 +1,38 @@ + esj.Trace = function(transport,options) { + options = options || {}; + var _d = { + info : false, + trace : false + }; + + this.options = defaults(options,_d); + this.transport = transport; + }; + + // TODO: Make this properly format the messages. Implement helper methods + esj.Trace.prototype = (function() { + + var info = function(msg) { + if (this.options.info){ + console.info(this.transport.options.hosts+" "+msg); + return msg; + } else { + return false; + } + }; + + var trace = function(msg) { + if (this.options.debug) { + console.log(this.transport.options.hosts+" "+msg); + return msg; + } else { + return false; + } + }; + + return { + info : info, + trace : trace + }; + + } ()); diff --git a/src/post.js b/src/post.js new file mode 100644 index 000000000..6b5c11558 --- /dev/null +++ b/src/post.js @@ -0,0 +1,3 @@ + + +}).call(this); \ No newline at end of file diff --git a/src/pre.js b/src/pre.js new file mode 100644 index 000000000..29cc61293 --- /dev/null +++ b/src/pre.js @@ -0,0 +1,20 @@ +(function() { + + 'use strict'; + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // save the previous version of ejs + var _esj = root && root.esj, + esj; + + // Create the esj object + if (typeof exports !== 'undefined') { + esj = exports; + } else { + esj = root.ejs = {}; + } \ No newline at end of file diff --git a/src/selector.js b/src/selector.js new file mode 100644 index 000000000..dbeb1b89a --- /dev/null +++ b/src/selector.js @@ -0,0 +1,12 @@ + /* Host selectors */ + + esj.Selector = { + roundRobin : function(hosts) { + hosts.unshift(hosts.pop()); + return hosts[0]; + }, + random : function(hosts) { + hosts = shuffle(hosts); + return hosts[0]; + } + }; diff --git a/src/serializer.js b/src/serializer.js new file mode 100644 index 000000000..05c2f7d2f --- /dev/null +++ b/src/serializer.js @@ -0,0 +1,3 @@ + /* Generic serializer, does nothing */ + + esj.Serializer = {}; \ No newline at end of file diff --git a/src/serializer/json.js b/src/serializer/json.js new file mode 100644 index 000000000..7a4248831 --- /dev/null +++ b/src/serializer/json.js @@ -0,0 +1,17 @@ + /* JSON serializer */ + + esj.Serializer.json = function() {}; + + esj.Serializer.json.prototype = (function() { + + return { + dump : function(obj) { + return JSON.stringify(obj); + }, + + load : function(string) { + return JSON.parse(string); + } + }; + + } ()); \ No newline at end of file diff --git a/src/shared.js b/src/shared.js new file mode 100644 index 000000000..b3aa61a72 --- /dev/null +++ b/src/shared.js @@ -0,0 +1,124 @@ + // (c) 2013 Rashid Khan, Elasticsearch BV + // Portions of this file are borrowed from Underscore js, + + // Set the aliases that underscore uses + var + slice = Array.prototype.slice, + toString = Object.prototype.toString, + hasOwnProp = Object.prototype.hasOwnProperty, + nativeForEach = Array.prototype.forEach, + nativeIsArray = Array.isArray, + nativeIndexOf = Array.prototype.indexOf, + has, + each, + defaults, + extend, + indexOf, + isUndefined, + shuffle, + queryString, + breaker = {}; + + // Has own property? + has = function (obj, key) { + return hasOwnProp.call(obj, key); + }; + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + each = function (obj, iterator, context) { + if (obj == null) { + return; + } + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) { + return; + } + } + } else { + for (var key in obj) { + if (has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) { + return; + } + } + } + } + }; + + // Fill in a given object with default properties. + defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Extend a given object with all the properties in passed-in object(s). + extend = function (obj) { + each(slice.call(arguments, 1), function (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Returns the index at which value can be found in the array, or -1 if + // value is not present in the array. + indexOf = function (array, item) { + if (array == null) { + return -1; + } + + var i = 0, l = array.length; + if (nativeIndexOf && array.indexOf === nativeIndexOf) { + return array.indexOf(item); + } + + for (; i < l; i++) { + if (array[i] === item) { + return i; + + } + } + + return -1; + }; + + // Is an object undefined? + isUndefined = function(obj) { + return obj === void 0; + }; + + // shuffle an array + shuffle = function(obj) { + var shuffled = [], rand; + each(obj, function(value, index, list) { + if (index === 0) { + shuffled[0] = value; + } else { + rand = Math.floor(Math.random() * (index + 1)); + shuffled[index] = shuffled[rand]; + shuffled[rand] = value; + } + }); + return shuffled; + }; + + // Takes an object and makes it into a query string + queryString = function(obj) { + var str = []; + each(obj,function(v,k){ + str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v)); + }); + return str.join("&"); + }; \ No newline at end of file diff --git a/src/transport.js b/src/transport.js new file mode 100644 index 000000000..d811126e7 --- /dev/null +++ b/src/transport.js @@ -0,0 +1,22 @@ + + esj.Transport = function (options) { + + options = options || {}; + + var _d = { + hosts : ['localhost:9200'], + //nodes_to_host_callback : construct_hosts_list, + sniff_on_start : false, + sniff_after_requests : 0, + sniff_on_connection_fail : false, + max_retries : 3, + selector : esj.Selector.roundRobin + }; + + // These are all unique to each instance of client + this.options = defaults(options,_d); + + // For conviences + this.selector = this.options.selector; + + }; \ No newline at end of file diff --git a/src/transport/elasticsearch-angular.js b/src/transport/elasticsearch-angular.js new file mode 100644 index 000000000..4c9f00c3a --- /dev/null +++ b/src/transport/elasticsearch-angular.js @@ -0,0 +1 @@ +/* angular.js transport client for elasticsearch-js */ \ No newline at end of file diff --git a/src/transport/elasticsearch-jquery.js b/src/transport/elasticsearch-jquery.js new file mode 100644 index 000000000..b59212850 --- /dev/null +++ b/src/transport/elasticsearch-jquery.js @@ -0,0 +1 @@ +/* jquery transport client for elasticsearch-js */ \ No newline at end of file diff --git a/src/transport/elasticsearch-node.js b/src/transport/elasticsearch-node.js new file mode 100644 index 000000000..19ef7f140 --- /dev/null +++ b/src/transport/elasticsearch-node.js @@ -0,0 +1,81 @@ + /* elasticsearch-js nodejs transport */ + + var http = require('http'); + + esj.Transport.prototype = (function() { + + // Split hostname:port into its repective parts + var splitHost = function(u) { + var s = u.split(':'); + return {host:s[0],port:s[1]}; + }; + + // Meta function for handling any http request that can have a body (PUT,POST,DELETE) + var performRequest = function (context,method, path, params, body, successcb, errorcb, retries) { + + var + //context = context, + host = splitHost(context.selector(context.options.hosts)), + options = { + host: host.host, + port: host.port, + path: path + '?' + queryString(params), + method: method, + headers: { + 'Content-Type': 'application/json' + } + }; + var request = http.request(options, function (res) { + + var data = ''; + res.setEncoding('utf8'); + + res.on('data', function (d) { + data = data + d; + }); + + res.on('end', function () { + + var response = { + data : data.charAt(0) === '{' ? JSON.parse(data) : data, + headers : res.headers, + status : res.statusCode + }; + + if (successcb != null && response.status < 300) { + successcb(response); + } else if (errorcb != null) { + errorcb(response); + } + }); + + }); + + if (errorcb != null) { + request.on('error', errorcb); + } + + if(method !== 'GET' && method !== 'HEAD') { + request.write(body); + } + + request.end(); + }; + + // Aliases to performRequest + var put = function (path, params, body, successcb, errorcb) {performRequest(this, 'PUT', path, params, body, successcb, errorcb);}; + var post = function (path, params, body, successcb, errorcb) {performRequest(this, 'POST', path, params, body, successcb, errorcb);}; + var del = function (path, params, body, successcb, errorcb) {performRequest(this, 'DELETE', path, params, body, successcb, errorcb);}; + var get = function (path, params, body, successcb, errorcb) {performRequest(this, 'GET', path, params, body, successcb, errorcb);}; + var head = function (path, params, body, successcb, errorcb) {performRequest(this, 'GET', path, params, body, successcb, errorcb);}; + + // Public functions + return { + get : get, + put : put, + post : post, + del : del, + head : head + }; + + } ()); diff --git a/test/test_logger.js b/test/test_logger.js new file mode 100644 index 000000000..2a26aa0c1 --- /dev/null +++ b/test/test_logger.js @@ -0,0 +1,58 @@ +/* node transport function tests */ +// TODO: add check to see if any data in ES, fail if so. +'use strict'; + +var esj = require('../dist/elasticsearch-node.js'); +var _c = new esj.Client(); + +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) +*/ + +exports.logger = { + setUp: function(done) { + // Suppress console messages (this, sadly, applies globally) + //console.error = console.warn = console.info = console.log = function() {} + done(); + }, + 'log': function(test) { + test.expect(5); + + // Test defaults + test.equal(_c.logger.error('error'),'error','Error should be logged'); + test.equal(_c.logger.warn('warn'),'warn','Warn should be logged'); + test.equal(_c.logger.info('info'),false,'Info should not be logged'); + test.equal(_c.logger.debug('debug'),false,'Debug should not be logged'); + + // Turn on info logging in first client + _c.logger.options.info = true; + test.equal(_c.logger.info('info'),'info','Info should be logged'); + + + test.done(); + }, + 'trace': function(test) { + test.expect(2); + + // Test defaults + test.equal(_c.tracer.info('info'),false,'Info should not be logged'); + test.equal(_c.tracer.trace('trace'),false,'Trace should not be logged'); + test.done(); + } +}; \ No newline at end of file diff --git a/test/test_selector.js b/test/test_selector.js new file mode 100644 index 000000000..0be0f95b9 --- /dev/null +++ b/test/test_selector.js @@ -0,0 +1,59 @@ +/* selector function tests */ +'use strict'; + +var esj = require('../dist/elasticsearch-node.js'); +var _c = new esj.Client(); + +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) +*/ + +exports.selector = { + setUp: function(done) { + done(); + }, + 'exists': function(test) { + test.expect(3); + test.ok(esj.Selector,'Should exist'); + test.ok(esj.Selector.roundRobin,'Should exist'); + test.ok(esj.Selector.random,'Should exist'); + test.done(); + }, + 'roundRobin' : function(test) { + test.expect(4); + var hosts = ['foo','bar','baz']; + test.equal(esj.Selector.roundRobin(hosts),'baz','Should be baz'); + test.equal(esj.Selector.roundRobin(hosts),'bar','Should be bar'); + test.equal(esj.Selector.roundRobin(hosts),'foo','Should be foo'); + + hosts = ['foo']; + test.equal(esj.Selector.roundRobin(hosts),'foo','Should be foo'); + + test.done(); + + }, + 'random' : function(test) { + test.expect(2); + var hosts = ['bar','baz','foo']; + test.ok(esj.Selector.roundRobin(hosts),'Should return something'); + // This is how underscore.js tests its shuffle, will have to suffice + test.deepEqual(hosts.sort(),['bar','baz','foo'],'Should contain the same elements'); + test.done(); + } +}; \ No newline at end of file diff --git a/test/test_serializer.js b/test/test_serializer.js new file mode 100644 index 000000000..ffefb9645 --- /dev/null +++ b/test/test_serializer.js @@ -0,0 +1,39 @@ +/* Serializer tests */ + +'use strict'; + +var esj = require('../dist/elasticsearch-node.js'); +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) +*/ + +exports.serializer = { + setUp: function(done) { + // setup here + done(); + }, + 'json': function(test) { + test.expect(2); + // Create serializer object + var _s = new esj.Serializer.json(); + test.equal(_s.dump({foo:true}), '{"foo":true}', 'should be \'{"foo":true}\''); + test.deepEqual(_s.load('{"foo":true}'), {foo:true}, 'should be {foo:true}'); + test.done(); + }, +}; \ No newline at end of file diff --git a/test/test_shared.js b/test/test_shared.js new file mode 100644 index 000000000..4cafa37c8 --- /dev/null +++ b/test/test_shared.js @@ -0,0 +1,68 @@ +/* Shared tests */ +/* +'use strict'; + +//var esj = require('../dist/elasticsearch-node.js'); +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) + +exports.shared = { + setUp: function(done) { + // setup here + done(); + }, + 'defaults': function(test) { + test.expect(1); + test.deepEqual(defaults({foo:1,bar:2},{bar:3,baz:4}),{foo:1,bar:2,baz:4},'should be foo:1,bar:2,baz:4') + test.done(); + }, + 'extend': function(test) { + test.expect(1); + test.deepEqual(extend({foo:1},{bar:2}),{foo:1,bar:2},'should be foo:1,bar:2') + test.done(); + }, + 'indexOf': function(test) { + test.expect(1); + test.equal(indexOf([1,2,3],3), 2, 'should be 2') + test.done(); + }, + 'queryString': function(test) { + test.expect(1); + test.equal(queryString({foo:'bar'}),'foo=bar','should be foo=bar') + test.done(); + }, + 'has': function(test) { + test.expect(1); + test.equal(has({foo:1},'foo'),true,'should be true') + test.done(); + }, + 'each': function(test) { + test.expect(1); + var str = ''; + each([1,2,3],function(v){str+=v;}) + test.equal(str,'123','should be 123') + test.done(); + }, + 'isUndefined': function (test) { + test.expect(1); + test.ok(isUndefined(undefined),'should be undefined') + test.done(); + } +}; +*/ \ No newline at end of file diff --git a/test/transport/test_node.js b/test/transport/test_node.js new file mode 100644 index 000000000..c656e1d0a --- /dev/null +++ b/test/transport/test_node.js @@ -0,0 +1,107 @@ +/* node transport function tests */ +// TODO: add check to see if any data in ES, fail if so. +'use strict'; + +var esj = require('../../dist/elasticsearch-node.js'); +var _c = new esj.Client(); + +/* + ======== A Handy Little Nodeunit Reference ======== + https://github.com/caolan/nodeunit + + Test methods: + test.expect(numAssertions) + test.done() + Test assertions: + test.ok(value, [message]) + test.equal(actual, expected, [message]) + test.notEqual(actual, expected, [message]) + test.deepEqual(actual, expected, [message]) + test.notDeepEqual(actual, expected, [message]) + test.strictEqual(actual, expected, [message]) + test.notStrictEqual(actual, expected, [message]) + test.throws(block, [error], [message]) + test.doesNotThrow(block, [error], [message]) + test.ifError(value) +*/ + +exports.transportNode = { + setUp: function(done) { + done(); + }, + 'hosts': function(test) { + test.expect(4); + + _c.options.hosts = ['foo:9200','bar:9200']; + test.equal(_c.options.hosts.length, 2, 'should be 2'); + test.equal(_c.options.hosts[1], 'bar:9200', 'should be bar:9200'); + _c.options.hosts = ['localhost:9200']; + test.equal(_c.options.hosts.length, 1, 'should be 1'); + test.equal(_c.options.hosts[0], 'localhost:9200', 'should be localhost:9200'); + + test.done(); + }, + 'options': function(test) { + test.expect(6); + var _n = new esj.Client(); + + test.equal(_c.options.sniff_on_start, false, 'should be false'); + test.equal(_c.options.sniff_after_requests, 0, 'should be 0'); + test.equal(_c.options.sniff_on_connection_fail, false, 'should be false'); + test.equal(_c.options.max_retries, 3, 'should be 3'); + + _c.options.max_retries = 5; + + test.equal(_c.options.max_retries, 5, '_c max_retries should be 5'); + test.equal(_n.options.max_retries, 3, '_n max_retries should be 3'); + + test.done(); + }, + // Create an index with put + 'put': function(test) { + test.expect(1); + _c.transport.put('/foo',{},'{"foo":1}',function(res) { + test.equal(res.data.ok,true,'index should be created'); + test.done(); + }); + }, + 'post': function(test) { + test.expect(1); + _c.transport.post('/foo/bar/baz',{},'{"foo":1}',function(res) { + test.equal(res.data.ok,true,'document should be created'); + test.done(); + }); + }, + 'get success': function(test) { + test.expect(1); + _c.transport.get('/foo/bar/baz',{},'',function(res) { + test.deepEqual(res.data._source,{foo:1},'should contain document source'); + test.done(); + }); + }, + 'get error': function(test) { + test.expect(1); + _c.transport.get('/foo/bar',{},'',function(data){},function(res) { + test.equal(res.data,'No handler found for uri [/foo/bar?] and method [GET]','End point should not exist'); + test.done(); + }); + }, + 'del': function(test) { + test.expect(1); + _c.transport.del('/foo',{},'',function(res) { + test.equal(res.data.ok,true,'index should be deleted'); + test.done(); + }); + }, + 'error callback': function(test) { + test.expect(1); + _c.options.hosts = ['localhost:1']; + _c.transport.get('/foo/bar',{},'',function(res){ + test.equal(res.data,'Test failed','Success function should not be called'); + test.done(); + },function(res) { + test.equal(res.code,'ECONNREFUSED','Connection should be refused'); + test.done(); + }); + } +}; \ No newline at end of file