Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

added tests (nodeunit)

  • Loading branch information...
commit 29479d92570ee58942566d9a2645d87babf3f5e7 1 parent bdb9eab
andris9 authored
Showing with 547 additions and 74 deletions.
  1. +30 −3 README.md
  2. +7 −12 gearman-connection.js
  3. +69 −59 gearnode.js
  4. +441 −0 tests.js
33 README.md
View
@@ -4,6 +4,17 @@
**NB!** this is usable beta but probably not yet ready for production, I'll yet have to do a lot of refactoring and optimization
+## Installation
+
+ npm install gearnode
+
+## Tests
+
+Tests are run with *nodeunit*
+
+ npm install nodeunit -g
+ nodeunit test.js
+
## Usage
### Worker
@@ -77,6 +88,22 @@ Example
console.log(exception);
});
+### Assign an ID for the Worker
+
+Worker ID's identify unique workers for monitoring Gearman.
+
+ worker.setWorkerId(id)
+
+Where
+
+ * **id** is a string that will act as the name for the worker
+
+Example
+
+ worker = new Gearman();
+ worker.addServer(); // use default values
+ worker.setWorkerId("my_worker");
+
### Submit a job
client.submitJob(func, payload[, options])
@@ -93,7 +120,7 @@ Possible option values
* **background** - if set to true, detach the job from the client (complete and error events will not be sent to the client)
* **priority** - indicates the priority of the job. Possible values "low", "normal" (default) and "high"
-Returns a Gearman Job object with the following events
+Returns a Client Job object with the following events
* **created** - when the function is queued by the server (params: handle value)
* **complete** - when the function returns (params: response data in encoding specified by the options value)
@@ -138,9 +165,9 @@ Where
* **payload** is the data sent by the client and in the encoding specified with *addFunction*
* **job** is a Gearman Job object that can be used to send data back
-#### Job object
+#### Worker Job object
-Job object has the following methods
+Worker Job object has the following methods
* **complete(response)** - send the result of the function back to the client
* **error(error)** - throw an exception (and end the job with *failed* status)
19 gearman-connection.js
View
@@ -16,11 +16,12 @@ function GearmanConnection(server, port){
this.queued_jobs = {};
+ this.workerId = null;
+
this.connected = false;
this.processing = false;
this.failed = false;
- this.retries = 0;
this.remainder = false;
this.debug = false;
@@ -140,12 +141,7 @@ GearmanConnection.prototype.processQueue = function(){
// if no connection yet, open one
if(!this.connected){
- if(this.retries<5){
- this.connect();
- }else{
- console.log("failed");
- this.failed = true;
- }
+ return this.connect();
return false;
}
@@ -265,15 +261,13 @@ GearmanConnection.prototype.connect = function(){
this.socket.on("connect", (function(){
this.connecting = false;
this.connected = true;
- this.retries = 0;
if(this.debug){
console.log("connected!");
- }
+ }
+
this.processQueue();
-
}).bind(this));
-
this.socket.on("end", this.close.bind(this));
this.socket.on("error", this.close.bind(this));
@@ -298,7 +292,7 @@ GearmanConnection.prototype.close = function(){
}
this.connected = false;
this.connecting = false;
- this.retries++;
+ this.emit("disconnect");
}
}
@@ -420,6 +414,7 @@ GearmanConnection.prototype.getExceptions = function(callback){
}
GearmanConnection.prototype.setWorkerId = function(id){
+ this.workerId = id;
this.sendCommand({
type: "SET_CLIENT_ID",
params: [id]
128 gearnode.js
View
@@ -10,6 +10,8 @@ function Gearman(){
this.function_names = [];
this.functions = {};
+
+ this.workerId = null;
}
utillib.inherits(Gearman, EventEmitter);
@@ -31,7 +33,6 @@ Gearman.prototype.addServer = function(server_name, server_port){
this.servers[server_name].connection.on("error", function(err){
console.log("Error with "+server_name);
- //console.log(err.message);
console.log(err.stack);
});
@@ -82,33 +83,6 @@ Gearman.prototype.addServer = function(server_name, server_port){
this.update(server_name);
}
-Gearman.prototype.runJob = function(server_name, handle, func_name, payload, uid){
- uid = uid || null;
- if(this.functions[func_name]){
-
- var encoding = this.functions[func_name].encoding.toLowerCase() || "buffer";
-
- switch(encoding){
- case "utf-8":
- case "ascii":
- case "base64":
- payload = payload && payload.toString(encoding) || "";
- break;
- case "number":
- payload = Number(payload && payload.toString("ascii") || "") || 0;
- break;
- case "buffer":
- default:
- // keep buffer
- }
-
- var job = new Gearman.GearmanWorker(handle, server_name, this);
- this.functions[func_name].func(payload, job);
- }else{
- this.servers[server_name].connection.jobError(handle, "Function "+func_name+" not found");
- }
-}
-
Gearman.prototype.removeServer = function(server_name){
var connection, pos;
@@ -130,6 +104,11 @@ Gearman.prototype.removeServer = function(server_name){
return true;
}
+Gearman.prototype.end = function(){
+ for(var i=this.server_names.length-1; i>=0; i--){
+ this.removeServer(this.server_names[i]);
+ }
+}
Gearman.prototype.update = function(server_name){
if(!server_name){
@@ -139,6 +118,10 @@ Gearman.prototype.update = function(server_name){
this.function_names.forEach((function(func_name){
this.register(func_name, server_name);
}).bind(this));
+
+ if(this.workerId){
+ this.setWorkerId(server_name, this.workerId);
+ }
}
Gearman.prototype.register = function(func_name, server_name){
@@ -172,38 +155,37 @@ Gearman.prototype.unregister = function(func_name, server_name){
}
}
+// WORKER FUNCTIONS
-Gearman.prototype.getExceptions = function(server_name, callback){
- var pos;
-
- if(!callback && typeof server_name =="function"){
- callback = server_name;
- server_name = null;
- }
-
- if(server_name){
- if(this.servers[server_name]){
-
- this.servers[server_name].connection.getExceptions((function(err, success){
- if(callback){
- return callback(err, success);
- }
- if(err){
- console.log("Server "+server_name+" responded with error: "+(err.message || err));
- }else{
- console.log("Exceptions are followed from "+server_name);
- }
- }).bind(this));
+Gearman.prototype.runJob = function(server_name, handle, func_name, payload, uid){
+ uid = uid || null;
+ if(this.functions[func_name]){
+
+ var encoding = this.functions[func_name].encoding.toLowerCase() || "buffer";
+
+ switch(encoding){
+ case "utf-8":
+ case "ascii":
+ case "base64":
+ payload = payload && payload.toString(encoding) || "";
+ break;
+ case "number":
+ payload = Number(payload && payload.toString("ascii") || "") || 0;
+ break;
+ case "buffer":
+ default:
+ // keep buffer
}
+
+ var job = new Gearman.GearmanWorker(handle, server_name, this);
+ this.functions[func_name].func(payload, job);
}else{
- this.server_names.forEach((function(server_name){
- if(server_name){
- this.getExceptions(server_name, callback);
- }
- }).bind(this))
+ this.servers[server_name].connection.jobError(handle, "Function "+func_name+" not found");
}
}
+
+
Gearman.prototype.setWorkerId = function(server_name, id){
var pos;
@@ -217,10 +199,10 @@ Gearman.prototype.setWorkerId = function(server_name, id){
if(server_name){
if(this.servers[server_name]){
-
this.servers[server_name].connection.setWorkerId(id);
}
}else{
+ this.workerId = id;
this.server_names.forEach((function(server_name){
if(server_name){
this.setWorkerId(server_name, id);
@@ -253,7 +235,6 @@ Gearman.prototype.addFunction = function(name, encoding, func){
func: func,
encoding: encoding || "buffer"
}
- this.function_names.push(name);
}
}
@@ -272,9 +253,36 @@ Gearman.prototype.removeFunction = function(name){
}
}
-Gearman.prototype.end = function(){
- for(var i=this.server_names.length-1; i>=0; i--){
- this.removeServer(this.server_names[i]);
+// CLIENT FUNCTIONS
+
+Gearman.prototype.getExceptions = function(server_name, callback){
+ var pos;
+
+ if(!callback && typeof server_name =="function"){
+ callback = server_name;
+ server_name = null;
+ }
+
+ if(server_name){
+ if(this.servers[server_name]){
+
+ this.servers[server_name].connection.getExceptions((function(err, success){
+ if(callback){
+ return callback(err, success);
+ }
+ if(err){
+ console.log("Server "+server_name+" responded with error: "+(err.message || err));
+ }else{
+ console.log("Exceptions are followed from "+server_name);
+ }
+ }).bind(this));
+ }
+ }else{
+ this.server_names.forEach((function(server_name){
+ if(server_name){
+ this.getExceptions(server_name, callback);
+ }
+ }).bind(this))
}
}
@@ -288,6 +296,8 @@ Gearman.prototype.submitJob = function(func_name, payload, options){
return new Gearman.GearmanJob(func_name, payload, options, server);
}
+
+// WORKER JOB
Gearman.GearmanJob = function(func_name, payload, options, server){
EventEmitter.call(this);
441 tests.js
View
@@ -0,0 +1,441 @@
+var Gearnode = require("./gearnode"),
+ GearmanConnection = require("./gearman-connection"),
+ testCase = require('nodeunit').testCase;
+
+
+exports.gearnode_instance = function(test){
+ var gearman = new Gearnode();
+ test.expect(1);
+ test.ok(gearman instanceof Gearnode, "Worker is a Gearnode instance");
+ test.done();
+}
+
+// ADD SERVER
+
+exports.server = {
+
+ add_one_server: function(test){
+ var gearman = new Gearnode();
+ gearman.addServer();
+
+ test.expect(2);
+ test.equal(gearman.server_names.length, 1, "One item in server_names array");
+ test.equal(Object.keys(gearman.servers).length, 1, "One item in gearman.servers object");
+ test.done();
+ },
+
+ add_one_server_multiple_times: function(test){
+ var gearman = new Gearnode();
+ gearman.addServer("localhost");
+ gearman.addServer("localhost");
+
+ test.expect(2);
+ test.equal(gearman.server_names.length, 1, "One item in server_names array");
+ test.equal(Object.keys(gearman.servers).length, 1, "One item in gearman.servers object");
+ test.done();
+ },
+
+ add_multiple_servers: function(test){
+ var gearman = new Gearnode();
+ gearman.addServer("localhost.local");
+ gearman.addServer("localhost.lan");
+
+ test.expect(2);
+ test.equal(gearman.server_names.length, 2, "Two items in server_names array");
+ test.equal(Object.keys(gearman.servers).length, 2, "Two items in gearman.servers object");
+ test.done();
+ },
+
+ server_instance: function(test){
+ var gearman = new Gearnode();
+ gearman.addServer();
+
+ test.expect(1);
+ test.ok(gearman.servers[gearman.server_names[0]].connection instanceof GearmanConnection, "Connection instance")
+ test.done();
+ }
+}
+
+// ADD FUNCTIONS
+
+exports.functions = {
+
+ add_one_function: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addFunction("foo", function(){});
+
+ test.expect(2);
+ test.equal(gearman.function_names.length, 1, "One item in function_names array");
+ test.equal(Object.keys(gearman.functions).length, 1, "One item in gearman.functions object");
+ test.done();
+ },
+
+ add_one_function_multiple_times: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addFunction("foo", function(){});
+ gearman.addFunction("foo", function(){});
+
+ test.expect(2);
+ test.equal(gearman.function_names.length, 1, "One item in function_names array");
+ test.equal(Object.keys(gearman.functions).length, 1, "One item in gearman.functions object");
+ test.done();
+ },
+
+ add_multiple_functions: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addFunction("foo", function(){});
+ gearman.addFunction("bar", function(){});
+
+ test.expect(2);
+ test.equal(gearman.function_names.length, 2, "Two items in function_names array");
+ test.equal(Object.keys(gearman.functions).length, 2, "Two items in gearman.functions object");
+ test.done();
+ },
+
+ function_properties: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addFunction("foo", function(){});
+ gearman.addFunction("bar", "string", function(){});
+
+ test.expect(6);
+ test.equal(gearman.function_names[0], "foo", "Function name for foo");
+ test.equal(gearman.function_names[1], "bar", "Function name for bar");
+ test.equal(typeof gearman.functions[gearman.function_names[0]].func, "function", "Function instance for foo");
+ test.equal(typeof gearman.functions[gearman.function_names[1]].func, "function", "Function instance for bar");
+ test.equal(gearman.functions[gearman.function_names[0]].encoding, "buffer", "Function encoding for foo");
+ test.equal(gearman.functions[gearman.function_names[1]].encoding, "string", "Function encoding for bar");
+ test.done();
+ }
+}
+
+// FUNCTIONS AND SERVES
+
+exports.functions_servers = {
+
+ add_function_to_existing_server: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addServer("foo");
+ gearman.addFunction("bar", function(){});
+
+ test.expect(1);
+ test.equal(gearman.servers["foo"].functions.length, 1, "One item in server functions array");
+ test.done();
+ },
+
+ add_function_before_server: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addFunction("bar", function(){});
+ gearman.addServer("foo");
+
+ test.expect(1);
+ test.equal(gearman.servers["foo"].functions.length, 1, "One item in server functions array");
+ test.done();
+ }
+}
+
+// WORKER ID
+
+exports.worker_id = {
+
+ set_worker_id: function(test){
+ var gearman = new Gearnode();
+
+ gearman.setWorkerId("bar");
+
+ test.expect(1);
+ test.equal(gearman.workerId, "bar", "Worker ID");
+ test.done();
+ },
+
+ set_worker_id_to_servers: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addServer("foo");
+ gearman.addServer("bar");
+
+ gearman.setWorkerId("baz");
+
+ test.expect(2);
+ test.equal(gearman.servers["foo"].connection.workerId, "baz", "Worker ID");
+ test.equal(gearman.servers["bar"].connection.workerId, "baz", "Worker ID");
+ test.done();
+ },
+
+ set_worker_id_before_server: function(test){
+ var gearman = new Gearnode();
+
+ gearman.addServer("foo");
+ gearman.setWorkerId("baz");
+ gearman.addServer("bar");
+
+ test.expect(2);
+ test.equal(gearman.servers["foo"].connection.workerId, "baz", "Worker ID");
+ test.equal(gearman.servers["bar"].connection.workerId, "baz", "Worker ID");
+ test.done();
+ }
+}
+
+module.exports.worker = testCase({
+ setUp: function (callback) {
+ this.worker = new Gearnode();
+ this.worker.addServer("localhost",7003);
+
+ this.client = new Gearnode();
+ this.client.addServer("localhost",7003);
+
+ this.worker.addFunction("upper", function(payload, job){
+ job.complete(payload.toString("utf-8").toUpperCase());
+ });
+
+ this.worker.addFunction("upper_utf8","utf-8", function(payload, job){
+ job.complete(payload.toUpperCase());
+ });
+
+ this.worker.addFunction("upper_base64","base64", function(payload, job){
+ job.complete(new Buffer(payload, "base64").toString("utf-8").toUpperCase());
+ });
+
+ this.worker.addFunction("getexception",function(payload, job){
+ job.error(new Error("Error happened"));
+ });
+
+ this.worker.addFunction("partial",function(payload, job){
+ var i=0;
+ job.data(i++);
+ job.data(i++);
+ job.data(i++);
+ job.complete("ready");
+ });
+
+ this.worker.addFunction("getwarning",function(payload, job){
+ job.warning("foo");
+ job.complete("bar");
+ });
+
+ this.worker.addFunction("getfail",function(payload, job){
+ job.fail();
+ });
+
+ callback();
+ },
+
+ tearDown: function (callback) {
+ // clean up
+ callback();
+ },
+
+ test_upper: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("upper","test");
+
+ job.on("complete", function(data){
+ test.equal(data.toString("utf-8"), "TEST", "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ test_upper_utf8: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("upper_utf8","test");
+
+ job.on("complete", function(data){
+ test.equal(data.toString("utf-8"), "TEST", "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ test_upper_base64: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("upper_base64","test");
+
+ job.on("complete", function(data){
+ test.equal(data.toString("utf-8"), "TEST", "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ test_upper_expect_utf8: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("upper","test", {encoding:"utf-8"});
+
+ job.on("complete", function(data){
+ test.equal(data, "TEST", "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ test_upper_expect_base64: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("upper","test", {encoding:"base64"});
+
+ job.on("complete", function(data){
+ test.equal(data, new Buffer("TEST","utf-8").toString("base64"), "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ getExceptions: function(test){
+ test.expect(2);
+ this.client.getExceptions((function(err, success){
+ test.ok(success,"Listening for exceptions");
+
+ var job = this.client.submitJob("getexception","test");
+
+ job.on("complete", function(data){
+ test.ok("false","No exceptions");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok("false","No exceptions");
+ test.done();
+ });
+
+ job.on("error", function(){
+ test.ok(true, "Function failed with error");
+ test.done();
+ });
+
+ }).bind(this));
+ },
+
+ test_partial_data: function(test){
+ test.expect(4);
+
+ var job = this.client.submitJob("partial", "test", {encoding:"utf-8"}),
+ i = 0;
+
+ job.on("complete", function(data){
+ test.equal(data, "ready", "Function success");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+
+ job.on("data", function(data){
+ test.equal(String(i++), data, "Function part OK");
+ test.done();
+ });
+ },
+
+ test_warning: function (test) {
+
+ test.expect(2);
+
+ var job = this.client.submitJob("getwarning","test", {encoding:"utf-8"});
+
+ job.on("complete", function(data){
+ test.equal(data, "bar", "Completed");
+ test.done();
+ });
+
+ job.on("warning", function(data){
+ test.equal(data, "foo", "Function warning");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(false, "Function failed");
+ test.done();
+ });
+
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ },
+
+ test_fail: function (test) {
+
+ test.expect(1);
+
+ var job = this.client.submitJob("getfail","test", {encoding:"utf-8"});
+
+ job.on("complete", function(data){
+ test.ok(false, "SHould not complete");
+ test.done();
+ });
+
+ job.on("fail", function(){
+ test.ok(true, "Function failed");
+ test.done();
+ });
+
+ job.on("error", function(){
+ test.ok(false, "Function failed with error");
+ test.done();
+ });
+ }
+});
+
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.