Merge branch 'master' into benchmark

This commit is contained in:
bmc 2013-04-04 13:06:53 -05:00
commit 3d7c31fa4a
35 changed files with 301 additions and 733 deletions

12
NEWS.md
View File

@ -1,10 +1,20 @@
### v1.0 - not released yet
All major and minor releases are briefly explained below.
For richer information consult the commit log on github with referenced pull requests.
We do not include break-fix version release in this file.
### v1.0
- remove deprecated functionality
- `pg.connect` now __requires__ 3 arguments
- Client#pauseDrain() / Client#resumeDrain removed
- numeric, decimal, and float data types no longer parsed into float before being returned. Will be returned from query results as `String`
### v0.15.0
- client now emits `end` when disconnected from back-end server
- if client is disconnected in the middle of a query, query receives an error
### v0.14.0

View File

@ -10,8 +10,6 @@ var Connection = require(__dirname + '/connection');
var CopyFromStream = require(__dirname + '/copystream').CopyFromStream;
var CopyToStream = require(__dirname + '/copystream').CopyToStream;
var deprecate = require('deprecate');
var Client = function(config) {
EventEmitter.call(this);
@ -171,6 +169,15 @@ Client.prototype.connect = function(callback) {
}
});
con.once('end', function() {
if(self.activeQuery) {
self.activeQuery.handleError(new Error('Stream unexpectedly ended during query execution'));
self.activeQuery = null;
}
self.emit('end');
});
con.on('notice', function(msg) {
self.emit('notice', msg);
});
@ -205,13 +212,7 @@ Client.prototype._pulseQueryQueue = function() {
this.activeQuery.submit(this.connection);
} else if(this.hasExecuted) {
this.activeQuery = null;
//TODO remove pauseDrain for v1.0
if(this._drainPaused > 0) {
this._drainPaused++;
}
else {
this.emit('drain');
}
this.emit('drain');
}
}
};
@ -255,32 +256,6 @@ Client.prototype.query = function(config, values, callback) {
return query;
};
//prevents client from otherwise emitting 'drain' event until 'resumeDrain' is
//called
Client.prototype.pauseDrain = function() {
deprecate('Client.prototype.pauseDrain is deprecated and will be removed it v1.0.0 (very soon)',
'please see the following for more details:',
'https://github.com/brianc/node-postgres/wiki/pg',
'https://github.com/brianc/node-postgres/issues/227',
'https://github.com/brianc/node-postgres/pull/274',
'feel free to get in touch via github if you have questions');
this._drainPaused = 1;
};
//resume raising 'drain' event
Client.prototype.resumeDrain = function() {
deprecate('Client.prototype.resumeDrain is deprecated and will be removed it v1.0.0 (very soon)',
'please see the following for more details:',
'https://github.com/brianc/node-postgres/wiki/pg',
'https://github.com/brianc/node-postgres/issues/227',
'https://github.com/brianc/node-postgres/pull/274',
'feel free to get in touch via github if you have questions');
if(this._drainPaused > 1) {
this.emit('drain');
}
this._drainPaused = 0;
};
Client.prototype.end = function() {
this.connection.end();
};

View File

@ -4,7 +4,7 @@ var EventEmitter = require('events').EventEmitter;
var util = require('util');
var utils = require(__dirname + '/utils');
var Writer = require(__dirname + '/writer');
var Writer = require('buffer-writer');
var Connection = function(config) {
EventEmitter.call(this);
@ -18,6 +18,7 @@ var Connection = function(config) {
this.parsedStatements = {};
this.writer = new Writer();
this.ssl = config.ssl || false;
this._ending = false;
};
util.inherits(Connection, EventEmitter);
@ -37,9 +38,18 @@ Connection.prototype.connect = function(port, host) {
});
this.stream.on('error', function(error) {
//don't raise ECONNRESET errors - they can & should be ignored
//during disconnect
if(self._ending && error.code == 'ECONNRESET') {
return;
}
self.emit('error', error);
});
this.stream.on('end', function() {
self.emit('end');
});
if(this.ssl) {
this.stream.once('data', function(buffer) {
self.setBuffer(buffer);
@ -259,6 +269,7 @@ Connection.prototype.end = function() {
//0x58 = 'X'
this.writer.add(emptyBuffer);
this._send(0x58);
this._ending = true;
};
Connection.prototype.describe = function(msg, more) {

View File

@ -33,12 +33,3 @@ module.exports = {
//pool log function / boolean
poolLog: false
};
var deprecate = require('deprecate');
//getter/setter to disable deprecation warnings
module.exports.__defineGetter__("hideDeprecationWarnings", function() {
return deprecate.silent;
});
module.exports.__defineSetter__("hideDeprecationWarnings", function(val) {
deprecate.silence = val;
});

View File

@ -1,25 +0,0 @@
var os = require('os');
var defaults = require(__dirname + '/defaults');
var hits = {
};
var deprecate = module.exports = function(methodName, message) {
if(defaults.hideDeprecationWarnings) return;
if(hits[deprecate.caller]) return;
hits[deprecate.caller] = true;
process.stderr.write(os.EOL);
process.stderr.write('\x1b[31;1m');
process.stderr.write('WARNING!!');
process.stderr.write(os.EOL);
process.stderr.write(methodName);
process.stderr.write(os.EOL);
for(var i = 1; i < arguments.length; i++) {
process.stderr.write(arguments[i]);
process.stderr.write(os.EOL);
}
process.stderr.write('\x1b[0m');
process.stderr.write(os.EOL);
process.stderr.write("You can silence these warnings with `require('pg').defaults.hideDeprecationWarnings = true`");
process.stderr.write(os.EOL);
process.stderr.write(os.EOL);
};

View File

@ -198,6 +198,15 @@ var clientBuilder = function(config) {
}
});
connection.on('_end', function() {
process.nextTick(function() {
if(connection._activeQuery) {
connection._activeQuery.handleError(new Error("Connection was ended during query"));
}
connection.emit('end');
});
});
connection.on('_readyForQuery', function() {
var q = this._activeQuery;
//a named query finished being prepared

View File

@ -3,8 +3,6 @@ var EventEmitter = require('events').EventEmitter;
var defaults = require(__dirname + '/defaults');
var genericPool = require('generic-pool');
var deprecate = require('deprecate');
var pools = {
//dictionary of all key:pool pairs
all: {},
@ -54,64 +52,17 @@ var pools = {
pool.connect = function(cb) {
pool.acquire(function(err, client) {
if(err) return cb(err, null, function() {/*NOOP*/});
//support both 2 (old) and 3 arguments
(cb.length > 2 ? newConnect : oldConnect)(pool, client, cb);
cb(null, client, function(err) {
if(err) {
pool.destroy(client);
} else {
pool.release(client);
}
});
});
};
return pool;
}
};
//the old connect method of the pool
//would automatically subscribe to the 'drain'
//event and automatically return the client to
//the pool once 'drain' fired once. This caused
//a bunch of problems, but for backwards compatibility
//we're leaving it in
var alarmDuration = 5000;
var errorMessage = [
'A client has been checked out from the pool for longer than ' + alarmDuration + ' ms.',
'You might have a leak!',
'You should use the following new way to check out clients','pg.connect(function(err, client, done)) {',
' //do something',
' done(); //call done() to signal you are finished with the client',
'}'
].join(require('os').EOL);
var oldConnect = function(pool, client, cb) {
deprecate('pg.connect(function(err, client) { ...}) is deprecated and will be removed it v1.0.0 (very soon)',
'instead, use pg.connect(function(err, client, done) { ... })',
'automatic releasing of clients back to the pool was a mistake and will be removed',
'please see the following for more details:',
'https://github.com/brianc/node-postgres/wiki/pg',
'https://github.com/brianc/node-postgres/issues/227',
'https://github.com/brianc/node-postgres/pull/274',
'feel free to get in touch via github if you have questions');
var tid = setTimeout(function() {
console.error(errorMessage);
}, alarmDuration);
var onError = function() {
clearTimeout(tid);
client.removeListener('drain', release);
};
var release = function() {
clearTimeout(tid);
pool.release(client);
client.removeListener('error', onError);
};
client.once('drain', release);
client.once('error', onError);
cb(null, client);
};
var newConnect = function(pool, client, cb) {
cb(null, client, function(err) {
if(err) {
pool.destroy(client);
} else {
pool.release(client);
}
});
};
module.exports = pools;

View File

@ -1,5 +1,3 @@
var deprecate = require('deprecate');
var parseBits = function(data, bits, offset, invert, callback) {
offset = offset || 0;
invert = invert || false;
@ -47,12 +45,6 @@ var parseBits = function(data, bits, offset, invert, callback) {
};
var parseFloatFromBits = function(data, precisionBits, exponentBits) {
deprecate('parsing and returning floats from PostgreSQL server is deprecated',
'JavaScript has a hard time with floats and there is precision loss which can cause',
'unexpected, hard to trace, potentially bad bugs in your program',
'for more information see the following:',
'https://github.com/brianc/node-postgres/pull/271',
'in node-postgres v1.0.0 all floats & decimals will be returned as strings');
var bias = Math.pow(2, exponentBits - 1) - 1;
var sign = parseBits(data, 1);
var exponent = parseBits(data, exponentBits, 1);

View File

@ -1,5 +1,3 @@
var deprecate = require('deprecate');
var arrayParser = require(__dirname + "/arrayParser.js");
//parses PostgreSQL server formatted date strings into javascript date objects
@ -78,18 +76,8 @@ var parseIntegerArray = function(val) {
};
var parseFloatArray = function(val) {
deprecate('parsing and returning floats from PostgreSQL server is deprecated',
'JavaScript has a hard time with floats and there is precision loss which can cause',
'unexpected, hard to trace, potentially bad bugs in your program',
'for more information see the following:',
'https://github.com/brianc/node-postgres/pull/271',
'in node-postgres v1.0.0 all floats & decimals will be returned as strings',
'feel free to get in touch via a github issue if you have any questions');
if(!val) { return null; }
var p = arrayParser.create(val, function(entry){
if(entry !== null) {
entry = parseFloat(entry, 10);
}
var p = arrayParser.create(val, function(entry) {
return entry;
});
@ -171,27 +159,11 @@ var parseInteger = function(val) {
return parseInt(val, 10);
};
var parseFloatAndWarn = function(val) {
deprecate('parsing and returning floats from PostgreSQL server is deprecated',
'JavaScript has a hard time with floats and there is precision loss which can cause',
'unexpected, hard to trace, potentially bad bugs in your program',
'for more information see the following:',
'https://github.com/brianc/node-postgres/pull/271',
'in node-postgres v1.0.0 all floats & decimals will be returned as strings');
return parseFloat(val);
};
var init = function(register) {
register(20, parseInteger);
register(21, parseInteger);
register(23, parseInteger);
register(26, parseInteger);
//TODO remove for v1.0
register(1700, parseFloatAndWarn);
//TODO remove for v1.0
register(700, parseFloatAndWarn);
//TODO remove for v1.0
register(701, parseFloatAndWarn);
register(16, parseBool);
register(1082, parseDate); // date
register(1114, parseDate); // timestamp without timezone

View File

@ -1,128 +0,0 @@
//binary data writer tuned for creating
//postgres message packets as effeciently as possible by reusing the
//same buffer to avoid memcpy and limit memory allocations
var Writer = function(size) {
this.size = size || 1024;
this.buffer = Buffer(this.size + 5);
this.offset = 5;
this.headerPosition = 0;
};
//resizes internal buffer if not enough size left
Writer.prototype._ensure = function(size) {
var remaining = this.buffer.length - this.offset;
if(remaining < size) {
var oldBuffer = this.buffer;
this.buffer = new Buffer(oldBuffer.length + size);
oldBuffer.copy(this.buffer);
}
};
Writer.prototype.addInt32 = function(num) {
this._ensure(4);
this.buffer[this.offset++] = (num >>> 24 & 0xFF);
this.buffer[this.offset++] = (num >>> 16 & 0xFF);
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
return this;
};
Writer.prototype.addInt16 = function(num) {
this._ensure(2);
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
return this;
};
//for versions of node requiring 'length' as 3rd argument to buffer.write
var writeString = function(buffer, string, offset, len) {
buffer.write(string, offset, len);
};
//overwrite function for older versions of node
if(Buffer.prototype.write.length === 3) {
writeString = function(buffer, string, offset, len) {
buffer.write(string, offset);
};
}
Writer.prototype.addCString = function(string) {
//just write a 0 for empty or null strings
if(!string) {
this._ensure(1);
} else {
var len = Buffer.byteLength(string);
this._ensure(len + 1); //+1 for null terminator
writeString(this.buffer, string, this.offset, len);
this.offset += len;
}
this.buffer[this.offset++] = 0; // null terminator
return this;
};
Writer.prototype.addChar = function(c) {
this._ensure(1);
writeString(this.buffer, c, this.offset, 1);
this.offset++;
return this;
};
Writer.prototype.addString = function(string) {
string = string || "";
var len = Buffer.byteLength(string);
this._ensure(len);
this.buffer.write(string, this.offset);
this.offset += len;
return this;
};
Writer.prototype.getByteLength = function() {
return this.offset - 5;
};
Writer.prototype.add = function(otherBuffer) {
this._ensure(otherBuffer.length);
otherBuffer.copy(this.buffer, this.offset);
this.offset += otherBuffer.length;
return this;
};
Writer.prototype.clear = function() {
this.offset = 5;
this.headerPosition = 0;
this.lastEnd = 0;
};
//appends a header block to all the written data since the last
//subsequent header or to the beginning if there is only one data block
Writer.prototype.addHeader = function(code, last) {
var origOffset = this.offset;
this.offset = this.headerPosition;
this.buffer[this.offset++] = code;
//length is everything in this packet minus the code
this.addInt32(origOffset - (this.headerPosition+1));
//set next header position
this.headerPosition = origOffset;
//make space for next header
this.offset = origOffset;
if(!last) {
this._ensure(5);
this.offset += 5;
}
};
Writer.prototype.join = function(code) {
if(code) {
this.addHeader(code, true);
}
return this.buffer.slice(code ? 0 : 5, this.offset);
};
Writer.prototype.flush = function(code) {
var result = this.join(code);
this.clear();
return result;
};
module.exports = Writer;

View File

@ -1,6 +1,6 @@
{
"name": "pg",
"version": "0.14.1",
"version": "1.0.0",
"description": "PostgreSQL client - pure javascript & libpq with the same API",
"keywords": [
"postgres",
@ -18,11 +18,11 @@
"author": "Brian Carlson <brian.m.carlson@gmail.com>",
"main": "./lib",
"dependencies": {
"generic-pool": "~2.0.2",
"deprecate": "~0.1.0"
"generic-pool": "2.0.2",
"buffer-writer": "1.0.0"
},
"devDependencies": {
"jshint": "git://github.com/jshint/jshint.git"
"jshint": "1.1.0"
},
"scripts": {
"test": "make test-all connectionString=pg://postgres@localhost:5432/postgres",

View File

@ -3,7 +3,7 @@ var helper = require(__dirname + '/../test/test-helper');
console.log();
console.log("testing ability to connect to '%j'", helper.config);
var pg = require(__dirname + '/../lib');
pg.connect(helper.config, function(err, client) {
pg.connect(helper.config, function(err, client, done) {
if(err !== null) {
console.error("Recieved connection error when attempting to contact PostgreSQL:");
console.error(err);
@ -18,6 +18,7 @@ pg.connect(helper.config, function(err, client) {
console.error(err);
process.exit(255);
}
done();
pg.end();
})
})

View File

@ -6,7 +6,7 @@
#include <stdlib.h>
#define LOG(msg) printf("%s\n",msg);
#define TRACE(msg) //printf("%s\n", msg);
#define TRACE(msg) //printf(%s\n, msg);
#define THROW(msg) return ThrowException(Exception::Error(String::New(msg)));
@ -434,12 +434,15 @@ protected:
if(revents & UV_READABLE) {
TRACE("revents & UV_READABLE");
TRACE("about to consume input");
if(PQconsumeInput(connection_) == 0) {
TRACE("could not read, terminating");
End();
EmitLastError();
//LOG("Something happened, consume input is 0");
return;
}
TRACE("Consumed");
//declare handlescope as this method is entered via a libuv callback
//and not part of the public v8 interface
@ -450,8 +453,11 @@ protected:
if (!this->copyInMode_ && !this->copyOutMode_ && PQisBusy(connection_) == 0) {
PGresult *result;
bool didHandleResult = false;
TRACE("PQgetResult");
while ((result = PQgetResult(connection_))) {
TRACE("HandleResult");
didHandleResult = HandleResult(result);
TRACE("PQClear");
PQclear(result);
if(!didHandleResult) {
//this means that we are in copy in or copy out mode
@ -469,6 +475,7 @@ protected:
}
PGnotify *notify;
TRACE("PQnotifies");
while ((notify = PQnotifies(connection_))) {
Local<Object> result = Object::New();
result->Set(channel_symbol, String::New(notify->relname));
@ -515,6 +522,7 @@ protected:
}
bool HandleResult(PGresult* result)
{
TRACE("PQresultStatus");
ExecStatusType status = PQresultStatus(result);
switch(status) {
case PGRES_TUPLES_OK:
@ -526,6 +534,7 @@ protected:
break;
case PGRES_FATAL_ERROR:
{
TRACE("HandleErrorResult");
HandleErrorResult(result);
return true;
}
@ -610,8 +619,15 @@ protected:
{
HandleScope scope;
//instantiate the return object as an Error with the summary Postgres message
Local<Object> msg = Local<Object>::Cast(Exception::Error(String::New(PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY))));
TRACE("ReadResultField");
const char* errorMessage = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
if(!errorMessage) {
//there is no error, it has already been consumed in the last
//read-loop callback
return;
}
Local<Object> msg = Local<Object>::Cast(Exception::Error(String::New(errorMessage)));
TRACE("AttachErrorFields");
//add the other information returned by Postgres to the error object
AttachErrorField(result, msg, severity_symbol, PG_DIAG_SEVERITY);
AttachErrorField(result, msg, code_symbol, PG_DIAG_SQLSTATE);
@ -625,6 +641,7 @@ protected:
AttachErrorField(result, msg, line_symbol, PG_DIAG_SOURCE_LINE);
AttachErrorField(result, msg, routine_symbol, PG_DIAG_SOURCE_FUNCTION);
Handle<Value> m = msg;
TRACE("EmitError");
Emit("_error", &m);
}
@ -638,9 +655,11 @@ protected:
void End()
{
TRACE("stopping read & write");
StopRead();
StopWrite();
DestroyConnection();
Emit("_end");
}
private:
@ -719,7 +738,7 @@ private:
void StopWrite()
{
TRACE("write STOP");
if(ioInitialized_) {
if(ioInitialized_ && writing_) {
uv_poll_stop(&write_watcher_);
writing_ = false;
}
@ -739,7 +758,7 @@ private:
void StopRead()
{
TRACE("read STOP");
if(ioInitialized_) {
if(ioInitialized_ && reading_) {
uv_poll_stop(&read_watcher_);
reading_ = false;
}

View File

@ -1,9 +1,5 @@
var helper = require(__dirname + '/../test-helper');
var pg = require(__dirname + '/../../../lib');
if(helper.args.native) {
pg = require(__dirname + '/../../../lib').native;
}
var pg = helper.pg;
var log = function() {
//console.log.apply(console, arguments);
@ -20,8 +16,9 @@ test('api', function() {
pg.connect(helper.config, function(err) {
assert.isNull(err);
arguments[1].emit('drain');
arguments[2]();
});
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.equal(err, null, "Failed to connect: " + helper.sys.inspect(err));
client.query('CREATE TEMP TABLE band(name varchar(100))');
@ -56,14 +53,14 @@ test('api', function() {
assert.equal(result.rows.pop().name, 'the flaming lips');
assert.equal(result.rows.pop().name, 'the beach boys');
sink.add();
done();
}))
}))
}))
})
test('executing nested queries', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
log("connected for nested queriese")
client.query('select now as now from NOW()', assert.calls(function(err, result) {
@ -73,6 +70,7 @@ test('executing nested queries', function() {
log('all nested queries recieved')
assert.ok('all queries hit')
sink.add();
done();
}))
}))
}))
@ -82,27 +80,29 @@ test('executing nested queries', function() {
test('raises error if cannot connect', function() {
var connectionString = "pg://sfalsdkf:asdf@localhost/ieieie";
log("trying to connect to invalid place for error")
pg.connect(connectionString, assert.calls(function(err, client) {
pg.connect(connectionString, assert.calls(function(err, client, done) {
assert.ok(err, 'should have raised an error')
log("invalid connection supplied error to callback")
sink.add();
done();
}))
})
test("query errors are handled and do not bubble if callback is provded", function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err)
log("checking for query error")
client.query("SELECT OISDJF FROM LEIWLISEJLSE", assert.calls(function(err, result) {
assert.ok(err);
log("query error supplied error to callback")
sink.add();
done();
}))
}))
})
test('callback is fired once and only once', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query("CREATE TEMP TABLE boom(name varchar(10))");
var callCount = 0;
@ -113,12 +113,13 @@ test('callback is fired once and only once', function() {
].join(";"), function(err, callback) {
assert.equal(callCount++, 0, "Call count should be 0. More means this callback fired more than once.");
sink.add();
done();
})
}))
})
test('can provide callback and config object', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query({
name: 'boom',
@ -126,12 +127,13 @@ test('can provide callback and config object', function() {
}, assert.calls(function(err, result) {
assert.isNull(err);
assert.equal(result.rows[0].now.getYear(), new Date().getYear())
done();
}))
}))
})
test('can provide callback and config and parameters', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
var config = {
text: 'select $1::text as val'
@ -140,12 +142,13 @@ test('can provide callback and config and parameters', function() {
assert.isNull(err);
assert.equal(result.rows.length, 1);
assert.equal(result.rows[0].val, 'hi');
done();
}))
}))
})
test('null and undefined are both inserted as NULL', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query("CREATE TEMP TABLE my_nulls(a varchar(1), b varchar(1), c integer, d integer, e date, f date)");
client.query("INSERT INTO my_nulls(a,b,c,d,e,f) VALUES ($1,$2,$3,$4,$5,$6)", [ null, undefined, null, undefined, null, undefined ]);
@ -158,6 +161,7 @@ test('null and undefined are both inserted as NULL', function() {
assert.isNull(result.rows[0].d);
assert.isNull(result.rows[0].e);
assert.isNull(result.rows[0].f);
done();
}))
}))
})

View File

@ -2,7 +2,7 @@ var helper = require(__dirname + "/test-helper");
var pg = helper.pg;
test('parsing array results', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query("CREATE TEMP TABLE why(names text[], numbors integer[])");
client.query('INSERT INTO why(names, numbors) VALUES(\'{"aaron", "brian","a b c" }\', \'{1, 2, 3}\')').on('error', console.log);
@ -23,7 +23,6 @@ test('parsing array results', function() {
assert.equal(names[0], 'aaron');
assert.equal(names[1], 'brian');
assert.equal(names[2], "a b c");
pg.end();
}))
})
@ -31,7 +30,6 @@ test('parsing array results', function() {
client.query("SELECT '{}'::text[] as names", assert.success(function(result) {
var names = result.rows[0].names;
assert.lengthIs(names, 0);
pg.end();
}))
})
@ -41,7 +39,6 @@ test('parsing array results', function() {
assert.lengthIs(names, 2);
assert.equal(names[0], 'joe,bob');
assert.equal(names[1], 'jim');
pg.end();
}))
})
@ -51,7 +48,6 @@ test('parsing array results', function() {
assert.lengthIs(names, 2);
assert.equal(names[0], '{');
assert.equal(names[1], '}');
pg.end();
}))
})
@ -63,7 +59,6 @@ test('parsing array results', function() {
assert.equal(names[1], null);
assert.equal(names[2], 'bob');
assert.equal(names[3], 'NULL');
pg.end();
}))
})
@ -74,7 +69,6 @@ test('parsing array results', function() {
assert.equal(names[0], 'joe\'');
assert.equal(names[1], 'jim');
assert.equal(names[2], 'bob"');
pg.end();
}))
})
@ -91,7 +85,6 @@ test('parsing array results', function() {
assert.equal(names[1][0], '2');
assert.equal(names[1][1], 'bob');
pg.end();
}))
})
@ -102,7 +95,6 @@ test('parsing array results', function() {
assert.equal(names[0], 1);
assert.equal(names[1], 2);
assert.equal(names[2], 3);
pg.end();
}))
})
@ -118,7 +110,6 @@ test('parsing array results', function() {
assert.equal(names[2][0], 3);
assert.equal(names[2][1], 100);
pg.end();
}))
})
@ -134,6 +125,7 @@ test('parsing array results', function() {
assert.equal(names[2][0], 3);
assert.equal(names[2][1], 100);
done();
pg.end();
}))
})

View File

@ -14,7 +14,7 @@ var prepareTable = function (client, callback) {
);
};
test('COPY FROM', function () {
pg.connect(helper.config, function (error, client) {
pg.connect(helper.config, function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
prepareTable(client, function () {
var stream = client.copyFrom("COPY copy_test (name, age) FROM stdin WITH CSV");
@ -30,7 +30,7 @@ test('COPY FROM', function () {
assert.lengthIs(result.rows, 1)
assert.equal(result.rows[0].sum, ROWS_TO_INSERT * (0 + ROWS_TO_INSERT -1)/2);
assert.equal(result.rows[0].count, ROWS_TO_INSERT);
pg.end(helper.config);
done();
});
}, "COPY FROM stream should emit close after query end");
stream.end();
@ -38,7 +38,7 @@ test('COPY FROM', function () {
});
});
test('COPY TO', function () {
pg.connect(helper.config, function (error, client) {
pg.connect(helper.config, function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
prepareTable(client, function () {
var stream = client.copyTo("COPY person (id, name, age) TO stdin WITH CSV");
@ -53,7 +53,7 @@ test('COPY TO', function () {
var lines = buf.toString().split('\n');
assert.equal(lines.length >= 0, true, "copy in should return rows saved by copy from");
assert.equal(lines[0].split(',').length, 3, "each line should consists of 3 fields");
pg.end(helper.config);
done();
}, "COPY IN stream should emit end event after all rows");
});
});
@ -61,7 +61,7 @@ test('COPY TO', function () {
test('COPY TO, queue queries', function () {
if(helper.config.native) return false;
pg.connect(helper.config, assert.calls(function (error, client) {
pg.connect(helper.config, assert.calls(function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
prepareTable(client, function () {
var query1Done = false,
@ -92,7 +92,7 @@ test('COPY TO, queue queries', function () {
var lines = buf.toString().split('\n');
assert.equal(lines.length >= 0, true, "copy in should return rows saved by copy from");
assert.equal(lines[0].split(',').length, 3, "each line should consists of 3 fields");
pg.end(helper.config);
done();
}, "COPY IN stream should emit end event after all rows");
});
}));
@ -105,7 +105,7 @@ test("COPY TO incorrect usage with large data", function () {
//but if there are not so much data, cancel message may be
//send after copy query ends
//so we need to test both situations
pg.connect(helper.config, assert.calls(function (error, client) {
pg.connect(helper.config, assert.calls(function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
//intentionally incorrect usage of copy.
//this has to report error in standart way, instead of just throwing exception
@ -116,7 +116,7 @@ test("COPY TO incorrect usage with large data", function () {
client.query("SELECT 1", assert.calls(function (error, result) {
assert.isNull(error, "incorrect copy usage should not break connection");
assert.ok(result, "incorrect copy usage should not break connection");
pg.end(helper.config);
done();
}));
})
);
@ -125,7 +125,7 @@ test("COPY TO incorrect usage with large data", function () {
test("COPY TO incorrect usage with small data", function () {
if(helper.config.native) return false;
pg.connect(helper.config, assert.calls(function (error, client) {
pg.connect(helper.config, assert.calls(function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
//intentionally incorrect usage of copy.
//this has to report error in standart way, instead of just throwing exception
@ -136,7 +136,7 @@ test("COPY TO incorrect usage with small data", function () {
client.query("SELECT 1", assert.calls(function (error, result) {
assert.isNull(error, "incorrect copy usage should not break connection");
assert.ok(result, "incorrect copy usage should not break connection");
pg.end(helper.config);
done();
}));
})
);
@ -144,7 +144,7 @@ test("COPY TO incorrect usage with small data", function () {
});
test("COPY FROM incorrect usage", function () {
pg.connect(helper.config, function (error, client) {
pg.connect(helper.config, function (error, client, done) {
assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error));
prepareTable(client, function () {
//intentionally incorrect usage of copy.
@ -156,6 +156,7 @@ test("COPY FROM incorrect usage", function () {
client.query("SELECT 1", assert.calls(function (error, result) {
assert.isNull(error, "incorrect copy usage should not break connection");
assert.ok(result, "incorrect copy usage should not break connection");
done();
pg.end(helper.config);
}));
})

View File

@ -1,55 +0,0 @@
var helper = require(__dirname + '/test-helper');
var pg = require(__dirname + '/../../../lib');
if(helper.args.native) {
pg = require(__dirname + '/../../../lib').native;
}
var testDrainOfClientWithPendingQueries = function() {
pg.connect(helper.config, assert.success(function(client) {
test('when there are pending queries and client is resumed', function() {
var drainCount = 0;
client.on('drain', function() {
drainCount++;
});
client.pauseDrain();
client.query('SELECT NOW()', function() {
client.query('SELECT NOW()', function() {
assert.equal(drainCount, 0);
process.nextTick(function() {
assert.equal(drainCount, 1);
pg.end();
});
});
client.resumeDrain();
assert.equal(drainCount, 0);
});
});
}));
};
pg.connect(helper.config, assert.success(function(client) {
var drainCount = 0;
client.on('drain', function() {
drainCount++;
});
test('pauseDrain and resumeDrain on simple client', function() {
client.pauseDrain();
client.resumeDrain();
process.nextTick(assert.calls(function() {
assert.equal(drainCount, 0);
test('drain is paused', function() {
client.pauseDrain();
client.query('SELECT NOW()', assert.success(function() {
process.nextTick(function() {
assert.equal(drainCount, 0);
client.resumeDrain();
assert.equal(drainCount, 1);
testDrainOfClientWithPendingQueries();
});
}));
});
}));
});
}));

View File

@ -158,6 +158,16 @@ test('multiple connection errors (gh#31)', function() {
var badConString = "tcp://aslkdfj:oi14081@"+helper.args.host+":"+helper.args.port+"/"+helper.args.database;
return false;
});
});
test('query receives error on client shutdown', function() {
var client = new Client(helper.config);
client.connect(assert.calls(function() {
client.query('SELECT pg_sleep(5)', assert.calls(function(err, res) {
assert(err);
}));
client.end();
assert.emits(client, 'end');
}));
});

View File

@ -1,6 +1,6 @@
var helper = require(__dirname + '/test-helper');
helper.pg.connect(helper.config, assert.success(function(client) {
helper.pg.connect(helper.config, assert.success(function(client, done) {
var types = require(__dirname + '/../../../lib/types');
//1231 = numericOID
types.setTypeParser(1700, function(){
@ -15,6 +15,7 @@ helper.pg.connect(helper.config, assert.success(function(client) {
client.query('SELECT * FROM bignumz', assert.success(function(result) {
assert.equal(result.rows[0].id, 'yes')
helper.pg.end();
done();
}))
}));

View File

@ -0,0 +1,23 @@
var helper = require(__dirname + '/test-helper');
var util = require('util');
test('error during query execution', function() {
var client = new Client(helper.args);
client.connect(assert.success(function() {
var sleepQuery = 'select pg_sleep(5)';
client.query(sleepQuery, assert.calls(function(err, result) {
assert(err);
client.end();
}));
var client2 = new Client(helper.args);
client2.connect(assert.success(function() {
var killIdleQuery = "SELECT procpid, (SELECT pg_terminate_backend(procpid)) AS killed FROM pg_stat_activity WHERE current_query = $1";
client2.query(killIdleQuery, [sleepQuery], assert.calls(function(err, res) {
assert.ifError(err);
assert.equal(res.rowCount, 1);
client2.end();
assert.emits(client2, 'end');
}));
}));
}));
});

View File

@ -2,7 +2,7 @@ var helper = require(__dirname + "/test-helper");
var pg = helper.pg;
test('should return insert metadata', function() {
pg.connect(helper.config, assert.calls(function(err, client) {
pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) {
@ -25,6 +25,7 @@ test('should return insert metadata', function() {
assert.emits(q, 'end', function(result) {
assert.equal(result.command, "INSERT");
assert.equal(result.rowCount, 1);
done();
});
}));

View File

@ -5,8 +5,7 @@ var sink = new helper.Sink(2, function() {
});
test('a single connection transaction', function() {
helper.pg.connect(helper.config, assert.calls(function(err, client) {
assert.isNull(err);
helper.pg.connect(helper.config, assert.success(function(client, done) {
client.query('begin');
@ -39,6 +38,7 @@ test('a single connection transaction', function() {
client.query(getZed, assert.calls(function(err, result) {
assert.isNull(err);
assert.empty(result.rows);
done();
sink.add();
}))
})
@ -46,8 +46,7 @@ test('a single connection transaction', function() {
})
test('gh#36', function() {
helper.pg.connect(helper.config, function(err, client) {
if(err) throw err;
helper.pg.connect(helper.config, assert.success(function(client, done) {
client.query("BEGIN");
client.query({
name: 'X',
@ -67,6 +66,7 @@ test('gh#36', function() {
}))
client.query("COMMIT", function() {
sink.add();
done();
})
})
}));
})

View File

@ -2,7 +2,7 @@ var helper = require(__dirname + '/test-helper');
var sink;
var testForTypeCoercion = function(type){
helper.pg.connect(helper.config, function(err, client) {
helper.pg.connect(helper.config, function(err, client, done) {
assert.isNull(err);
client.query("create temp table test_type(col " + type.name + ")", assert.calls(function(err, result) {
assert.isNull(err);
@ -31,6 +31,7 @@ var testForTypeCoercion = function(type){
client.query('drop table test_type', function() {
sink.add();
done();
});
})
}));
@ -56,15 +57,14 @@ var types = [{
name: 'bool',
values: [true, false, null]
},{
//TODO get some actual huge numbers here
name: 'numeric',
values: [-12.34, 0, 12.34, null]
values: ['-12.34', '0', '12.34', null]
},{
name: 'real',
values: [101.1, 0, -101.3, null]
values: ['101.1', '0', '-101.3', null]
},{
name: 'double precision',
values: [-1.2, 0, 1.2, null]
values: ['-1.2', '0', '1.2', null]
},{
name: 'timestamptz',
values: [null]
@ -82,7 +82,7 @@ var types = [{
// ignore some tests in binary mode
if (helper.config.binary) {
types = types.filter(function(type) {
return !(type.name in {'real':1, 'timetz':1, 'time':1});
return !(type.name in {'real':1, 'timetz':1, 'time':1, 'numeric': 1, 'double precision': 1});
});
}
@ -133,7 +133,7 @@ test("timestampz round trip", function() {
client.on('drain', client.end.bind(client));
});
helper.pg.connect(helper.config, assert.calls(function(err, client) {
helper.pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query('select null as res;', assert.calls(function(err, res) {
assert.isNull(err);
@ -143,6 +143,7 @@ helper.pg.connect(helper.config, assert.calls(function(err, client) {
assert.isNull(err);
assert.strictEqual(res.rows[0].res, null);
sink.add();
done();
})
}))

View File

@ -8,12 +8,13 @@ test('disconnects', function() {
helper.pg.end();
});
[helper.config, helper.config, helper.config, helper.config].forEach(function(config) {
helper.pg.connect(config, function(err, client) {
helper.pg.connect(config, function(err, client, done) {
assert.isNull(err);
client.query("SELECT * FROM NOW()", function(err, result) {
process.nextTick(function() {
assert.equal(called, false, "Should not have disconnected yet")
sink.add();
done();
})
})
})

View File

@ -1,28 +1,29 @@
var helper = require(__dirname + "/../test-helper");
var pg = require(__dirname + "/../../../lib");
helper.pg = pg;
pg = pg;
//first make pool hold 2 clients
helper.pg.defaults.poolSize = 2;
pg.defaults.poolSize = 2;
var killIdleQuery = 'SELECT procpid, (SELECT pg_terminate_backend(procpid)) AS killed FROM pg_stat_activity WHERE current_query LIKE \'<IDLE>\'';
//get first client
helper.pg.connect(helper.config, assert.success(function(client) {
pg.connect(helper.config, assert.success(function(client, done) {
client.id = 1;
helper.pg.connect(helper.config, assert.success(function(client2) {
pg.connect(helper.config, assert.success(function(client2, done2) {
client2.id = 2;
done2();
//subscribe to the pg error event
assert.emits(helper.pg, 'error', function(error, brokenClient) {
assert.emits(pg, 'error', function(error, brokenClient) {
assert.ok(error);
assert.ok(brokenClient);
assert.equal(client.id, brokenClient.id);
helper.pg.end();
});
//kill the connection from client
client2.query(killIdleQuery, assert.success(function(res) {
//check to make sure client connection actually was killed
assert.lengthIs(res.rows, 1);
pg.end();
}));
}));
}));

View File

@ -3,10 +3,11 @@ var helper = require(__dirname + '/test-helper');
helper.pg.defaults.poolIdleTimeout = 200;
test('idle timeout', function() {
helper.pg.connect(helper.config, assert.calls(function(err, client) {
helper.pg.connect(helper.config, assert.calls(function(err, client, done) {
assert.isNull(err);
client.query('SELECT NOW()');
//just let this one time out
//test will hang if pool doesn't timeout
done();
}));
});

View File

@ -8,12 +8,13 @@ helper.pg.defaults.port = helper.args.port;
helper.pg.defaults.database = helper.args.database;
helper.pg.defaults.poolSize = 1;
helper.pg.connect(assert.calls(function(err, client) {
helper.pg.connect(assert.calls(function(err, client, done) {
assert.isNull(err);
client.query('SELECT NOW()');
client.once('drain', function() {
setTimeout(function() {
helper.pg.end();
done();
}, 10);
});

View File

@ -5,26 +5,30 @@ test('query with non-text as first parameter throws error', function() {
var client = new Client(helper.config);
client.connect();
assert.emits(client, 'connect', function() {
assert.throws(function() {
client.query({text:{fail: true}});
})
client.end();
})
})
assert.emits(client, 'end', function() {
assert.throws(function() {
client.query({text:{fail: true}});
});
});
});
});
test('parameterized query with non-text as first parameter throws error', function() {
var client = new Client(helper.config);
client.connect();
assert.emits(client, 'connect', function() {
assert.throws(function() {
client.query({
text: {fail: true},
values: [1, 2]
})
})
client.end();
})
})
assert.emits(client, 'end', function() {
assert.throws(function() {
client.query({
text: {fail: true},
values: [1, 2]
})
});
});
});
});
var connect = function(callback) {
var client = new Client(helper.config);
@ -37,24 +41,28 @@ var connect = function(callback) {
test('parameterized query with non-array for second value', function() {
test('inline', function() {
connect(function(client) {
assert.throws(function() {
client.query("SELECT *", "LKSDJF")
})
client.end();
})
})
assert.emits(client, 'end', function() {
assert.throws(function() {
client.query("SELECT *", "LKSDJF")
});
});
});
});
test('config', function() {
connect(function(client) {
assert.throws(function() {
client.query({
text: "SELECT *",
values: "ALSDKFJ"
})
})
client.end();
})
})
})
assert.emits(client, 'end', function() {
assert.throws(function() {
client.query({
text: "SELECT *",
values: "ALSDKFJ"
});
});
});
});
});
});

View File

@ -7,8 +7,6 @@ var BufferList = require(__dirname+'/buffer-list')
var Connection = require(__dirname + '/../lib/connection');
require(__dirname + '/../lib').defaults.hideDeprecationWarnings = true;
Client = require(__dirname + '/../lib').Client;
process.on('uncaughtException', function(d) {
@ -98,13 +96,25 @@ assert.empty = function(actual) {
};
assert.success = function(callback) {
return assert.calls(function(err, arg) {
if(err) {
console.log(err);
}
assert.isNull(err);
callback(arg);
})
if(callback.length === 1 || callback.length === 0) {
return assert.calls(function(err, arg) {
if(err) {
console.log(err);
}
assert.isNull(err);
callback(arg);
});
} else if (callback.length === 2) {
return assert.calls(function(err, arg1, arg2) {
if(err) {
console.log(err);
}
assert.isNull(err);
callback(arg1, arg2);
});
} else {
throw new Error('need to preserve arrity of wrapped function');
}
}
assert.throws = function(offender) {
@ -127,13 +137,26 @@ var expect = function(callback, timeout) {
assert.ok(executed, "Expected execution of function to be fired");
}, timeout || 5000)
return function(err, queryResult) {
clearTimeout(id);
if (err) {
assert.ok(err instanceof Error, "Expected errors to be instances of Error: " + sys.inspect(err));
if(callback.length < 3) {
return function(err, queryResult) {
clearTimeout(id);
if (err) {
assert.ok(err instanceof Error, "Expected errors to be instances of Error: " + sys.inspect(err));
}
callback.apply(this, arguments)
}
callback.apply(this, arguments)
} else if(callback.length == 3) {
return function(err, arg1, arg2) {
clearTimeout(id);
if (err) {
assert.ok(err instanceof Error, "Expected errors to be instances of Error: " + sys.inspect(err));
}
callback.apply(this, arguments)
}
} else {
throw new Error("Unsupported arrity " + callback.length);
}
}
assert.calls = expect;

View File

@ -50,63 +50,3 @@ test('drain', function() {
});
});
});
test('with drain paused', function() {
//mock out a fake connection
var con = new Connection({stream: "NO"});
con.connect = function() {
con.emit('connect');
};
con.query = function() {
};
var client = new Client({connection:con});
client.connect();
var drainCount = 0;
client.on('drain', function() {
drainCount++;
});
test('normally unpaused', function() {
con.emit('readyForQuery');
client.query('boom');
assert.emits(client, 'drain', function() {
assert.equal(drainCount, 1);
});
con.emit('readyForQuery');
});
test('pausing', function() {
test('unpaused with no queries in between', function() {
client.pauseDrain();
client.resumeDrain();
assert.equal(drainCount, 1);
});
test('paused', function() {
test('resumeDrain after empty', function() {
client.pauseDrain();
client.query('asdf');
con.emit('readyForQuery');
assert.equal(drainCount, 1);
client.resumeDrain();
assert.equal(drainCount, 2);
});
test('resumDrain while still pending', function() {
client.pauseDrain();
client.query('asdf');
client.query('asdf1');
con.emit('readyForQuery');
client.resumeDrain();
assert.equal(drainCount, 2);
con.emit('readyForQuery');
assert.equal(drainCount, 3);
});
});
});
});

View File

@ -0,0 +1,26 @@
var helper = require(__dirname + '/test-helper');
var Connection = require(__dirname + '/../../../lib/connection');
var Client = require(__dirname + '/../../../lib/client');
test('emits end when not in query', function() {
var stream = new (require('events').EventEmitter)();
stream.write = function() {
//NOOP
}
var client = new Client({connection: new Connection({stream: stream})});
client.connect(assert.calls(function() {
client.query('SELECT NOW()', assert.calls(function(err, result) {
assert(err);
}));
}));
assert.emits(client, 'end');
client.connection.emit('connect');
process.nextTick(function() {
client.connection.emit('readyForQuery');
assert.equal(client.queryQueue.length, 0);
assert(client.activeQuery, 'client should have issued query');
process.nextTick(function() {
stream.emit('end');
});
});
});

View File

@ -43,19 +43,19 @@ test('typed results', function() {
format: 'text',
dataTypeID: 1700,
actual: '12.34',
expected: 12.34
expected: '12.34'
},{
name: 'real/float4',
dataTypeID: 700,
format: 'text',
actual: '123.456',
expected: 123.456
expected: '123.456'
},{
name: 'double precision / float8',
format: 'text',
dataTypeID: 701,
actual: '1.2',
expected: 1.2
expected: '1.2'
},{
name: 'boolean true',
format: 'text',

View File

@ -1,10 +1,30 @@
var helper = require(__dirname + '/test-helper');
var Connection = require(__dirname + '/../../../lib/connection');
var con = new Connection({stream: new MemoryStream()});
test("connection emits stream errors", function() {
var con = new Connection({stream: new MemoryStream()});
assert.emits(con, 'error', function(err) {
assert.equal(err.message, "OMG!");
});
con.connect();
con.stream.emit('error', new Error("OMG!"));
});
test('connection emits ECONNRESET errors during normal operation', function() {
var con = new Connection({stream: new MemoryStream()});
con.connect();
assert.emits(con, 'error', function(err) {
assert.equal(err.code, 'ECONNRESET');
});
var e = new Error('Connection Reset');
e.code = 'ECONNRESET';
con.stream.emit('error', e);
});
test('connection does not emit ECONNRESET errors during disconnect', function() {
var con = new Connection({stream: new MemoryStream()});
con.connect();
var e = new Error('Connection Reset');
e.code = 'ECONNRESET';
con.end();
con.stream.emit('error', e);
});

View File

@ -68,19 +68,6 @@ test('pool follows defaults', function() {
assert.equal(p.getPoolSize(), defaults.poolSize);
});
test('pool#connect with 2 parameters (legacy, for backwards compat)', function() {
var p = pools.getOrCreate(poolId++);
p.connect(assert.success(function(client) {
assert.ok(client);
assert.equal(p.availableObjectsCount(), 0);
assert.equal(p.getPoolSize(), 1);
client.emit('drain');
assert.equal(p.availableObjectsCount(), 1);
assert.equal(p.getPoolSize(), 1);
p.destroyAllNow();
}));
});
test('pool#connect with 3 parameters', function() {
var p = pools.getOrCreate(poolId++);
var tid = setTimeout(function() {
@ -88,7 +75,7 @@ test('pool#connect with 3 parameters', function() {
}, 100);
p.connect(function(err, client, done) {
clearTimeout(tid);
assert.equal(err, null);
assert.ifError(err, null);
assert.ok(client);
assert.equal(p.availableObjectsCount(), 0);
assert.equal(p.getPoolSize(), 1);
@ -104,9 +91,9 @@ test('pool#connect with 3 parameters', function() {
test('on client error, client is removed from pool', function() {
var p = pools.getOrCreate(poolId++);
p.connect(assert.success(function(client) {
p.connect(assert.success(function(client, done) {
assert.ok(client);
client.emit('drain');
done();
assert.equal(p.availableObjectsCount(), 1);
assert.equal(p.getPoolSize(), 1);
//error event fires on pool BEFORE pool.destroy is called with client

View File

@ -1,196 +0,0 @@
require(__dirname + "/test-helper");
var Writer = require(__dirname + "/../../lib/writer");
test('adding int32', function() {
var testAddingInt32 = function(int, expectedBuffer) {
test('writes ' + int, function() {
var subject = new Writer();
var result = subject.addInt32(int).join();
assert.equalBuffers(result, expectedBuffer);
})
}
testAddingInt32(0, [0, 0, 0, 0]);
testAddingInt32(1, [0, 0, 0, 1]);
testAddingInt32(256, [0, 0, 1, 0]);
test('writes largest int32', function() {
//todo need to find largest int32 when I have internet access
return false;
})
test('writing multiple int32s', function() {
var subject = new Writer();
var result = subject.addInt32(1).addInt32(10).addInt32(0).join();
assert.equalBuffers(result, [0, 0, 0, 1, 0, 0, 0, 0x0a, 0, 0, 0, 0]);
})
test('having to resize the buffer', function() {
test('after resize correct result returned', function() {
var subject = new Writer(10);
subject.addInt32(1).addInt32(1).addInt32(1)
assert.equalBuffers(subject.join(), [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1])
})
})
})
test('int16', function() {
test('writes 0', function() {
var subject = new Writer();
var result = subject.addInt16(0).join();
assert.equalBuffers(result, [0,0]);
})
test('writes 400', function() {
var subject = new Writer();
var result = subject.addInt16(400).join();
assert.equalBuffers(result, [1, 0x90])
})
test('writes many', function() {
var subject = new Writer();
var result = subject.addInt16(0).addInt16(1).addInt16(2).join();
assert.equalBuffers(result, [0, 0, 0, 1, 0, 2])
})
test('resizes if internal buffer fills up', function() {
var subject = new Writer(3);
var result = subject.addInt16(2).addInt16(3).join();
assert.equalBuffers(result, [0, 2, 0, 3])
})
})
test('cString', function() {
test('writes empty cstring', function() {
var subject = new Writer();
var result = subject.addCString().join();
assert.equalBuffers(result, [0])
})
test('writes two empty cstrings', function() {
var subject = new Writer();
var result = subject.addCString("").addCString("").join();
assert.equalBuffers(result, [0, 0])
})
test('writes non-empty cstring', function() {
var subject = new Writer();
var result = subject.addCString("!!!").join();
assert.equalBuffers(result, [33, 33, 33, 0]);
})
test('resizes if reached end', function() {
var subject = new Writer(3);
var result = subject.addCString("!!!").join();
assert.equalBuffers(result, [33, 33, 33, 0]);
})
test('writes multiple cstrings', function() {
var subject = new Writer();
var result = subject.addCString("!").addCString("!").join();
assert.equalBuffers(result, [33, 0, 33, 0]);
})
})
test('writes char', function() {
var subject = new Writer(2);
var result = subject.addChar('a').addChar('b').addChar('c').join();
assert.equalBuffers(result, [0x61, 0x62, 0x63])
})
test('gets correct byte length', function() {
var subject = new Writer(5);
assert.equal(subject.getByteLength(), 0)
subject.addInt32(0)
assert.equal(subject.getByteLength(), 4)
subject.addCString("!")
assert.equal(subject.getByteLength(), 6)
})
test('can add arbitrary buffer to the end', function() {
var subject = new Writer(4);
subject.addCString("!!!")
var result = subject.add(Buffer("@@@")).join();
assert.equalBuffers(result, [33, 33, 33, 0, 0x40, 0x40, 0x40]);
})
test('can write normal string', function() {
var subject = new Writer(4);
var result = subject.addString("!").join();
assert.equalBuffers(result, [33]);
test('can write cString too', function() {
var result = subject.addCString("!").join();
assert.equalBuffers(result, [33, 33, 0]);
test('can resize', function() {
var result = subject.addString("!!").join();
assert.equalBuffers(result, [33, 33, 0, 33, 33]);
})
})
})
test('clearing', function() {
var subject = new Writer();
subject.addCString("@!!#!#");
subject.addInt32(10401);
subject.clear();
assert.equalBuffers(subject.join(), []);
test('can keep writing', function() {
var joinedResult = subject.addCString("!").addInt32(9).addInt16(2).join();
assert.equalBuffers(joinedResult, [33, 0, 0, 0, 0, 9, 0, 2]);
test('flush', function() {
var flushedResult = subject.flush();
test('returns result', function() {
assert.equalBuffers(flushedResult, [33, 0, 0, 0, 0, 9, 0, 2])
})
test('clears the writer', function() {
assert.equalBuffers(subject.join(), [])
assert.equalBuffers(subject.flush(), [])
})
})
})
})
test("resizing to much larger", function() {
var subject = new Writer(2);
var string = "!!!!!!!!";
var result = subject.addCString(string).flush();
assert.equalBuffers(result, [33, 33, 33, 33, 33, 33, 33, 33, 0])
})
test("flush", function() {
test('added as a hex code to a full writer', function() {
var subject = new Writer(2);
var result = subject.addCString("!").flush(0x50)
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]);
})
test('added as a hex code to a non-full writer', function() {
var subject = new Writer(10).addCString("!");
var joinedResult = subject.join(0x50);
var result = subject.flush(0x50);
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]);
})
test('added as a hex code to a buffer which requires resizing', function() {
var result = new Writer(2).addCString("!!!!!!!!").flush(0x50);
assert.equalBuffers(result, [0x50, 0, 0, 0, 0x0D, 33, 33, 33, 33, 33, 33, 33, 33, 0]);
})
})
test("header", function() {
test('adding two packets with headers', function() {
var subject = new Writer(10).addCString("!");
subject.addHeader(0x50);
subject.addCString("!!");
subject.addHeader(0x40);
subject.addCString("!");
var result = subject.flush(0x10);
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0, 0x40, 0, 0, 0, 7, 33, 33, 0, 0x10, 0, 0, 0, 6, 33, 0 ]);
})
})