From ec0d0beff29087a809d85da61faadb44401fcdc1 Mon Sep 17 00:00:00 2001 From: bmc Date: Mon, 21 Jan 2013 15:09:44 -0600 Subject: [PATCH 1/7] build libpq connection string & support domain socket --- lib/connection-parameters.js | 29 ++++++ .../connection-parameters/creation-tests.js | 95 +++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index e006e58..c2c5a8d 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -1,3 +1,5 @@ +var dns = require('dns'); + var defaults = require(__dirname + '/defaults'); var val = function(key, config) { @@ -33,6 +35,33 @@ var ConnectionParameters = function(config) { this.password = val('password', config); this.binary = val('binary', config); this.ssl = config.ssl || defaults.ssl; + this.isDomainSocket = (!(this.host||'').indexOf('/')); +}; + +var add = function(params, config, paramName) { + var value = config[paramName]; + if(value) { + params.push(paramName+"='"+value+"'"); + } +}; + +ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { + var params = [] + add(params, this, 'user'); + add(params, this, 'password'); + add(params, this, 'port'); + if(this.database) { + params.push("dbname='" + this.database + "'"); + } + if(this.isDomainSocket) { + params.push("host=" + this.host); + return cb(null, params.join(' ')); + } + dns.lookup(this.host, function(err, address) { + if(err) return cb(err, null); + params.push("hostaddr=" + address); + return cb(null, params.join(' ')); + }); }; module.exports = ConnectionParameters; diff --git a/test/unit/connection-parameters/creation-tests.js b/test/unit/connection-parameters/creation-tests.js index e8306b6..b4707c9 100644 --- a/test/unit/connection-parameters/creation-tests.js +++ b/test/unit/connection-parameters/creation-tests.js @@ -26,6 +26,7 @@ var compare = function(actual, expected, type) { test('ConnectionParameters initializing from defaults', function() { var subject = new ConnectionParameters(); compare(subject, defaults, 'defaults'); + assert.ok(subject.isDomainSocket === false); }); test('ConnectionParameters initializing from config', function() { @@ -43,4 +44,98 @@ test('ConnectionParameters initializing from config', function() { }; var subject = new ConnectionParameters(config); compare(subject, config, 'config'); + assert.ok(subject.isDomainSocket === false); }); + +test('initializing with unix domain socket', function() { + var subject = new ConnectionParameters('/var/run/pg.sock'); + assert.ok(subject.isDomainSocket); + assert.equal(subject.host, '/var/run/pg.sock'); +}); + +test('libpq connection string building', function() { + var checkForPart = function(array, part) { + assert.ok(array.indexOf(part) > -1, array.join(" ") + " did not contain " + part); + } + + test('builds simple string', function() { + var config = { + user: 'brian', + password: 'xyz', + port: 888, + host: 'localhost', + database: 'bam' + } + var subject = new ConnectionParameters(config); + subject.getLibpqConnectionString(assert.calls(function(err, constring) { + assert.isNull(err); + var parts = constring.split(" "); + checkForPart(parts, "user='brian'"); + checkForPart(parts, "password='xyz'"); + checkForPart(parts, "port='888'"); + checkForPart(parts, "hostaddr=127.0.0.1"); + checkForPart(parts, "dbname='bam'"); + })); + }); + + test('builds dns string', function() { + var config = { + user: 'brian', + password: 'asdf', + port: 5432, + host: 'localhost' + }; + var subject = new ConnectionParameters(config); + subject.getLibpqConnectionString(assert.calls(function(err, constring) { + assert.isNull(err); + var parts = constring.split(" "); + checkForPart(parts, "user='brian'"); + checkForPart(parts, "hostaddr=127.0.0.1"); + })); + }); + + test('error when dns fails', function() { + var config = { + user: 'brian', + password: 'asf', + port: 5432, + host: 'asdlfkjasldfkksfd#!$!!!!..com' + }; + var subject = new ConnectionParameters(config); + subject.getLibpqConnectionString(assert.calls(function(err, constring) { + assert.ok(err); + assert.isNull(constring) + })); + }); + + test('connecting to unix domain socket', function() { + var config = { + user: 'brian', + password: 'asf', + port: 5432, + host: '/var/run/pgsockbla' + }; + var subject = new ConnectionParameters(config); + subject.getLibpqConnectionString(assert.calls(function(err, constring) { + assert.isNull(err); + var parts = constring.split(" "); + checkForPart(parts, "user='brian'"); + checkForPart(parts, "host=/var/run/pgsockbla"); + })); + }); + + test('password contains < and/or > characters', function () { + return false; + var sourceConfig = { + user:'brian', + password: 'helloe', + port: 5432, + host: 'localhost', + database: 'postgres' + } + var connectionString = 'pg://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; + var subject = new ConnectionParameters(connectionString); + assert.equal(subject.password, sourceConfig.password); + }); + +}) From 6da25609cfc268e60e6bce0bbf1dc87970b32935 Mon Sep 17 00:00:00 2001 From: bmc Date: Mon, 21 Jan 2013 15:44:34 -0600 Subject: [PATCH 2/7] fix native compile warnings --- src/binding.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/binding.cc b/src/binding.cc index bc76cd4..dbef24e 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -494,7 +494,7 @@ protected: } if (copied == 0) { //wait for next read ready - //result was not handled copmpletely + //result was not handled completely return false; } else if (copied == -1) { this->copyOutMode_ = false; @@ -503,6 +503,7 @@ protected: this->copyOutMode_ = false; return true; } + return true; } bool HandleResult(PGresult* result) { @@ -546,6 +547,7 @@ protected: printf("YOU SHOULD NEVER SEE THIS! PLEASE OPEN AN ISSUE ON GITHUB! Unrecogized query status: %s\n", PQresStatus(status)); break; } + return true; } void EmitCommandMetaData(PGresult* result) From 9dad56a54e19432d59d5dc6ad79a906217a6ba97 Mon Sep 17 00:00:00 2001 From: bmc Date: Mon, 21 Jan 2013 15:44:55 -0600 Subject: [PATCH 3/7] add more functionality to connection parameters --- lib/connection-parameters.js | 14 ++++++++++++- .../connection-parameters/creation-tests.js | 21 ++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index c2c5a8d..5a6dc43 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -1,4 +1,5 @@ var dns = require('dns'); +var path = require('path'); var defaults = require(__dirname + '/defaults'); @@ -35,6 +36,7 @@ var ConnectionParameters = function(config) { this.password = val('password', config); this.binary = val('binary', config); this.ssl = config.ssl || defaults.ssl; + //a domain socket begins with '/' this.isDomainSocket = (!(this.host||'').indexOf('/')); }; @@ -54,7 +56,7 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { params.push("dbname='" + this.database + "'"); } if(this.isDomainSocket) { - params.push("host=" + this.host); + params.push("host=" + this.getDomainSocketName()); return cb(null, params.join(' ')); } dns.lookup(this.host, function(err, address) { @@ -64,4 +66,14 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { }); }; +ConnectionParameters.prototype.getDomainSocketName = function() { + var filename = '.s.PGSQL.' + this.port; + + //if host is full path to socket fd with port number, just return it + if(this.host.indexOf(filename) > -1) return this.host; + + //otherwise, build it from host + standard filename + port + return path.join(this.host, filename); +}; + module.exports = ConnectionParameters; diff --git a/test/unit/connection-parameters/creation-tests.js b/test/unit/connection-parameters/creation-tests.js index b4707c9..caba634 100644 --- a/test/unit/connection-parameters/creation-tests.js +++ b/test/unit/connection-parameters/creation-tests.js @@ -48,9 +48,20 @@ test('ConnectionParameters initializing from config', function() { }); test('initializing with unix domain socket', function() { - var subject = new ConnectionParameters('/var/run/pg.sock'); + var subject = new ConnectionParameters('/var/run/'); assert.ok(subject.isDomainSocket); - assert.equal(subject.host, '/var/run/pg.sock'); + assert.equal(subject.host, '/var/run/'); +}); + +test('builds domain socket', function() { + var subject = new ConnectionParameters({ + host: '/var/run/', + port: 1234 + }); + assert.equal(subject.getDomainSocketName(), '/var/run/.s.PGSQL.1234'); + subject.host = '/tmp'; + assert.equal(subject.getDomainSocketName(), '/tmp/.s.PGSQL.1234'); + assert.equal(subject.getDomainSocketName(), '/tmp/.s.PGSQL.1234'); }); test('libpq connection string building', function() { @@ -113,14 +124,14 @@ test('libpq connection string building', function() { user: 'brian', password: 'asf', port: 5432, - host: '/var/run/pgsockbla' + host: '/tmp/' }; var subject = new ConnectionParameters(config); subject.getLibpqConnectionString(assert.calls(function(err, constring) { assert.isNull(err); var parts = constring.split(" "); checkForPart(parts, "user='brian'"); - checkForPart(parts, "host=/var/run/pgsockbla"); + checkForPart(parts, "host=/tmp/.s.PGSQL.5432"); })); }); @@ -138,4 +149,4 @@ test('libpq connection string building', function() { assert.equal(subject.password, sourceConfig.password); }); -}) +}); From 1e3107aa55426608193d61dda90ca87cc451346f Mon Sep 17 00:00:00 2001 From: bmc Date: Mon, 21 Jan 2013 16:14:19 -0600 Subject: [PATCH 4/7] use ConnectionParameters for js client properties --- lib/client.js | 18 ++++++++++-------- test/integration/client/configuration-tests.js | 12 ++++++++++++ test/unit/client/configuration-tests.js | 12 ++++++------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/client.js b/lib/client.js index 43d20a9..4ec0b0e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -2,6 +2,7 @@ var crypto = require('crypto'); var EventEmitter = require('events').EventEmitter; var util = require('util'); +var ConnectionParameters = require(__dirname + '/connection-parameters'); var Query = require(__dirname + '/query'); var utils = require(__dirname + '/utils'); var defaults = require(__dirname + '/defaults'); @@ -10,20 +11,21 @@ var CopyFromStream = require(__dirname + '/copystream').CopyFromStream; var CopyToStream = require(__dirname + '/copystream').CopyToStream; var Client = function(config) { EventEmitter.call(this); - if(typeof config === 'string') { - config = utils.normalizeConnectionInfo(config) - } + + this.connectionParameters = new ConnectionParameters(config); + this.user = this.connectionParameters.user; + this.database = this.connectionParameters.database; + this.port = this.connectionParameters.port; + this.host = this.connectionParameters.host; + this.password = this.connectionParameters.password; + config = config || {}; - this.user = config.user || defaults.user; - this.database = config.database || defaults.database; - this.port = config.port || defaults.port; - this.host = config.host || defaults.host; + this.connection = config.connection || new Connection({ stream: config.stream, ssl: config.ssl }); this.queryQueue = []; - this.password = config.password || defaults.password; this.binary = config.binary || defaults.binary; this.encoding = 'utf8'; this.processID = null; diff --git a/test/integration/client/configuration-tests.js b/test/integration/client/configuration-tests.js index c641b30..e922a4e 100644 --- a/test/integration/client/configuration-tests.js +++ b/test/integration/client/configuration-tests.js @@ -1,6 +1,13 @@ var helper = require(__dirname + '/test-helper'); var pg = helper.pg; +//clear process.env +var realEnv = {}; +for(var key in process.env) { + realEnv[key] = process.env[key]; + if(!key.indexOf('PG')) delete process.env[key]; +} + test('default values', function() { assert.same(pg.defaults,{ user: process.env.USER, @@ -44,3 +51,8 @@ if(!helper.args.native) { }) } + +//restore process.env +for(var key in realEnv) { + process.env[key] = realEnv[key]; +} diff --git a/test/unit/client/configuration-tests.js b/test/unit/client/configuration-tests.js index cb60119..7d09fa1 100644 --- a/test/unit/client/configuration-tests.js +++ b/test/unit/client/configuration-tests.js @@ -4,8 +4,8 @@ test('client settings', function() { test('defaults', function() { var client = new Client(); - assert.equal(client.user, process.env.USER); - assert.equal(client.database, process.env.USER); + assert.equal(client.user, process.env['PGUSER'] || process.env.USER); + assert.equal(client.database, process.env['PGDATABASE'] || process.env.USER); assert.equal(client.port, 5432); }); @@ -41,11 +41,11 @@ test('initializing from a config string', function() { test('when not including all values the defaults are used', function() { var client = new Client("pg://host1") - assert.equal(client.user, process.env.USER) - assert.equal(client.password, null) + assert.equal(client.user, process.env['PGUSER'] || process.env.USER) + assert.equal(client.password, process.env['PGPASSWORD'] || null) assert.equal(client.host, "host1") - assert.equal(client.port, 5432) - assert.equal(client.database, process.env.USER) + assert.equal(client.port, process.env['PGPORT'] || 5432) + assert.equal(client.database, process.env['PGDATABASE'] || process.env.USER) }) From edfa3b5cdcb292a86a5f35e3d29a63ffc6c4421a Mon Sep 17 00:00:00 2001 From: bmc Date: Mon, 21 Jan 2013 16:30:59 -0600 Subject: [PATCH 5/7] add assert.calls to async functions within tests --- test/integration/client/copy-tests.js | 38 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/test/integration/client/copy-tests.js b/test/integration/client/copy-tests.js index 577ee96..eccf49e 100644 --- a/test/integration/client/copy-tests.js +++ b/test/integration/client/copy-tests.js @@ -4,6 +4,7 @@ if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } var ROWS_TO_INSERT = 1000; + var prepareTable = function (client, callback) { client.query( 'CREATE TEMP TABLE copy_test (id SERIAL, name CHARACTER VARYING(10), age INT)', @@ -13,8 +14,9 @@ var prepareTable = function (client, callback) { }) ); }; + test('COPY FROM', function () { - pg.connect(helper.config, function (error, client) { + pg.connect(helper.config, assert.calls(function (error, client) { 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"); @@ -25,20 +27,21 @@ test('COPY FROM', function () { stream.write( String(Date.now() + Math.random()).slice(0,10) + ',' + i + '\n'); } assert.emits(stream, 'close', function () { - client.query("SELECT count(*), sum(age) from copy_test", function (err, result) { + client.query("SELECT count(*), sum(age) from copy_test", assert.calls(function (err, result) { assert.equal(err, null, "Query should not fail"); 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); - }); + })); }, "COPY FROM stream should emit close after query end"); stream.end(); }); - }); + })); }); + test('COPY TO', function () { - pg.connect(helper.config, function (error, client) { + pg.connect(helper.config, assert.calls(function (error, client) { 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"); @@ -56,10 +59,11 @@ test('COPY TO', function () { pg.end(helper.config); }, "COPY IN stream should emit end event after all rows"); }); - }); + })); }); + test('COPY TO, queue queries', function () { - pg.connect(helper.config, function (error, client) { + pg.connect(helper.config, assert.calls(function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { var query1Done = false, @@ -73,10 +77,10 @@ test('COPY TO, queue queries', function () { //imitate long query, to make impossible, //that copy query end callback runs after //second query callback - client.query("SELECT pg_sleep(5)", function () { + client.query("SELECT pg_sleep(1)", assert.calls(function () { query2Done = true; assert.ok(copyQueryDone && query2Done, "second query has to be executed after others"); - }); + })); var buf = new Buffer(0); stream.on('error', function (error) { assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error)); @@ -93,15 +97,16 @@ test('COPY TO, queue queries', function () { pg.end(helper.config); }, "COPY IN stream should emit end event after all rows"); }); - }); + })); }); + test("COPY TO incorrect usage with large data", function () { //when many data is loaded from database (and it takes a lot of time) //there are chance, that query will be canceled before it ends //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, function (error, client) { + pg.connect(helper.config, assert.calls(function (error, client) { 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,10 +121,11 @@ test("COPY TO incorrect usage with large data", function () { })); }) ); - }); + })); }); + test("COPY TO incorrect usage with small data", function () { - pg.connect(helper.config, function (error, client) { + pg.connect(helper.config, assert.calls(function (error, client) { 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 @@ -134,11 +140,11 @@ 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, assert.calls(function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { //intentionally incorrect usage of copy. @@ -155,6 +161,6 @@ test("COPY FROM incorrect usage", function () { }) ); }); - }); + })); }); From 020607c49c0b542221afba05cd78cb5450f0c555 Mon Sep 17 00:00:00 2001 From: bmc Date: Tue, 22 Jan 2013 23:17:10 -0600 Subject: [PATCH 6/7] return false as default/fall-through value --- src/binding.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index dbef24e..d170599 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -503,7 +503,7 @@ protected: this->copyOutMode_ = false; return true; } - return true; + return false; } bool HandleResult(PGresult* result) { @@ -533,7 +533,7 @@ protected: { this->copyInMode_ = true; Emit("copyInResponse"); - return false; + return false; } break; case PGRES_COPY_OUT: From 113b6298e26a1fa5532e49554edbbd176808ef67 Mon Sep 17 00:00:00 2001 From: bmc Date: Tue, 22 Jan 2013 23:23:47 -0600 Subject: [PATCH 7/7] use ConnectionParameters with native bindings and remove unused util functions --- lib/native/index.js | 16 +-- lib/utils.js | 88 ---------------- test/cli.js | 13 +-- test/integration/client/copy-tests.js | 26 ++--- test/unit/utils-tests.js | 138 -------------------------- 5 files changed, 24 insertions(+), 257 deletions(-) diff --git a/lib/native/index.js b/lib/native/index.js index eb9307d..c0394b4 100644 --- a/lib/native/index.js +++ b/lib/native/index.js @@ -1,5 +1,7 @@ //require the c++ bindings & export to javascript var EventEmitter = require('events').EventEmitter; + +var ConnectionParameters = require(__dirname + '/../connection-parameters'); var utils = require(__dirname + "/../utils"); var CopyFromStream = require(__dirname + '/../copystream').CopyFromStream; var CopyToStream = require(__dirname + '/../copystream').CopyToStream; @@ -28,7 +30,7 @@ var nativeConnect = p.connect; p.connect = function(cb) { var self = this; - utils.buildLibpqConnectionString(this._config, function(err, conString) { + this.connectionParameters.getLibpqConnectionString(function(err, conString) { if(err) { return cb ? cb(err) : self.emit('error', err); } @@ -143,13 +145,13 @@ var clientBuilder = function(config) { connection._queryQueue = []; connection._namedQueries = {}; connection._activeQuery = null; - connection._config = utils.normalizeConnectionInfo(config); + connection.connectionParameters = new ConnectionParameters(config); //attach properties to normalize interface with pure js client - connection.user = connection._config.user; - connection.password = connection._config.password; - connection.database = connection._config.database; - connection.host = connection._config.host; - connection.port = connection._config.port; + connection.user = connection.connectionParameters.user; + connection.password = connection.connectionParameters.password; + connection.database = connection.connectionParameters.database; + connection.host = connection.connectionParameters.host; + connection.port = connection.connectionParameters.port; connection.on('connect', function() { connection._connected = true; connection._pulseQueryQueue(true); diff --git a/lib/utils.js b/lib/utils.js index b644163..366ebe6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -13,88 +13,6 @@ if(typeof events.EventEmitter.prototype.once !== 'function') { }; } -var parseConnectionString = function(str) { - //unix socket - if(str.charAt(0) === '/') { - return { host: str }; - } - var result = url.parse(str); - var config = {}; - config.host = result.hostname; - config.database = result.pathname ? result.pathname.slice(1) : null - var auth = (result.auth || ':').split(':'); - config.user = auth[0]; - config.password = auth[1]; - config.port = result.port; - return config; -}; - -//allows passing false as property to remove it from config -var norm = function(config, propName) { - config[propName] = (config[propName] || (config[propName] === false ? undefined : defaults[propName])) -}; - -//normalizes connection info -//which can be in the form of an object -//or a connection string -var normalizeConnectionInfo = function(config) { - switch(typeof config) { - case 'object': - norm(config, 'user'); - norm(config, 'password'); - norm(config, 'host'); - norm(config, 'port'); - norm(config, 'database'); - return config; - case 'string': - return normalizeConnectionInfo(parseConnectionString(config)); - default: - throw new Error("Unrecognized connection config parameter: " + config); - } -}; - - -var add = function(params, config, paramName) { - var value = config[paramName]; - if(value) { - params.push(paramName+"='"+value+"'"); - } -} - -//builds libpq specific connection string -//from a supplied config object -//the config object conforms to the interface of the config object -//accepted by the pure javascript client -var getLibpgConString = function(config, callback) { - if(typeof config == 'object') { - var params = [] - add(params, config, 'user'); - add(params, config, 'password'); - add(params, config, 'port'); - if(config.database) { - params.push("dbname='" + config.database + "'"); - } - if(config.host) { - if (!config.host.indexOf("/")) { - params.push("host=" + config.host); - } else { - if(config.host != 'localhost' && config.host != '127.0.0.1') { - //do dns lookup - return require('dns').lookup(config.host, function(err, address) { - if(err) return callback(err, null); - params.push("hostaddr="+address) - callback(null, params.join(" ")) - }) - } - params.push("hostaddr=127.0.0.1 "); - } - } - callback(null, params.join(" ")); - } else { - throw new Error("Unrecognized config type for connection"); - } -} - //converts values from javascript types //to their 'raw' counterparts for use as a postgres parameter //note: you can override this function to provide your own conversion mechanism @@ -126,12 +44,6 @@ function normalizeQueryConfig (config, values, callback) { } module.exports = { - normalizeConnectionInfo: normalizeConnectionInfo, - //only exported here to make testing of this method possible - //since it contains quite a bit of logic and testing for - //each connection scenario in an integration test is impractical - buildLibpqConnectionString: getLibpgConString, - parseConnectionString: parseConnectionString, prepareValue: prepareValue, normalizeQueryConfig: normalizeQueryConfig } diff --git a/test/cli.js b/test/cli.js index 45bb5ae..b6ca963 100644 --- a/test/cli.js +++ b/test/cli.js @@ -1,14 +1,5 @@ -var config = {}; -if(process.argv[2]) { - config = require(__dirname + '/../lib/utils').parseConnectionString(process.argv[2]); -} -//TODO use these environment variables in lib/ code -//http://www.postgresql.org/docs/8.4/static/libpq-envars.html -config.host = config.host || process.env['PGHOST'] || process.env['PGHOSTADDR']; -config.port = config.port || process.env['PGPORT']; -config.database = config.database || process.env['PGDATABASE']; -config.user = config.user || process.env['PGUSER']; -config.password = config.password || process.env['PGPASSWORD']; +var ConnectionParameters = require(__dirname + '/../lib/connection-parameters'); +var config = new ConnectionParameters(process.argv[2]); for(var i = 0; i < process.argv.length; i++) { switch(process.argv[i].toLowerCase()) { diff --git a/test/integration/client/copy-tests.js b/test/integration/client/copy-tests.js index eccf49e..d53360b 100644 --- a/test/integration/client/copy-tests.js +++ b/test/integration/client/copy-tests.js @@ -4,7 +4,6 @@ if(helper.args.native) { pg = require(__dirname + '/../../../lib').native; } var ROWS_TO_INSERT = 1000; - var prepareTable = function (client, callback) { client.query( 'CREATE TEMP TABLE copy_test (id SERIAL, name CHARACTER VARYING(10), age INT)', @@ -14,9 +13,8 @@ var prepareTable = function (client, callback) { }) ); }; - test('COPY FROM', function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { 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"); @@ -27,21 +25,20 @@ test('COPY FROM', function () { stream.write( String(Date.now() + Math.random()).slice(0,10) + ',' + i + '\n'); } assert.emits(stream, 'close', function () { - client.query("SELECT count(*), sum(age) from copy_test", assert.calls(function (err, result) { + client.query("SELECT count(*), sum(age) from copy_test", function (err, result) { assert.equal(err, null, "Query should not fail"); 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); - })); + }); }, "COPY FROM stream should emit close after query end"); stream.end(); }); - })); + }); }); - test('COPY TO', function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { 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"); @@ -59,10 +56,11 @@ test('COPY TO', function () { pg.end(helper.config); }, "COPY IN stream should emit end event after all rows"); }); - })); + }); }); test('COPY TO, queue queries', function () { + if(helper.config.native) return false; pg.connect(helper.config, assert.calls(function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { @@ -77,10 +75,10 @@ test('COPY TO, queue queries', function () { //imitate long query, to make impossible, //that copy query end callback runs after //second query callback - client.query("SELECT pg_sleep(1)", assert.calls(function () { + client.query("SELECT pg_sleep(1)", function () { query2Done = true; assert.ok(copyQueryDone && query2Done, "second query has to be executed after others"); - })); + }); var buf = new Buffer(0); stream.on('error', function (error) { assert.ok(false, "COPY TO stream should not emit errors" + helper.sys.inspect(error)); @@ -101,6 +99,7 @@ test('COPY TO, queue queries', function () { }); test("COPY TO incorrect usage with large data", function () { + if(helper.config.native) return false; //when many data is loaded from database (and it takes a lot of time) //there are chance, that query will be canceled before it ends //but if there are not so much data, cancel message may be @@ -125,6 +124,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) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); //intentionally incorrect usage of copy. @@ -144,7 +144,7 @@ test("COPY TO incorrect usage with small data", function () { }); test("COPY FROM incorrect usage", function () { - pg.connect(helper.config, assert.calls(function (error, client) { + pg.connect(helper.config, function (error, client) { assert.equal(error, null, "Failed to connect: " + helper.sys.inspect(error)); prepareTable(client, function () { //intentionally incorrect usage of copy. @@ -161,6 +161,6 @@ test("COPY FROM incorrect usage", function () { }) ); }); - })); + }); }); diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index 03a3d38..30ff9d2 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -21,144 +21,6 @@ test("EventEmitter.once", function(t) { }); -test('normalizing connection info', function() { - test('with objects', function() { - test('empty object uses defaults', function() { - var input = {}; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, defaults.user); - assert.equal(output.database, defaults.database); - assert.equal(output.port, defaults.port); - assert.equal(output.host, defaults.host); - assert.equal(output.password, defaults.password); - }); - - test('full object ignores defaults', function() { - var input = { - user: 'test1', - database: 'test2', - port: 'test3', - host: 'test4', - password: 'test5' - }; - assert.equal(utils.normalizeConnectionInfo(input), input); - }); - - test('connection string', function() { - test('non-unix socket', function() { - test('uses defaults', function() { - var input = ""; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, defaults.user); - assert.equal(output.database, defaults.database); - assert.equal(output.port, defaults.port); - assert.equal(output.host, defaults.host); - assert.equal(output.password, defaults.password); - }); - test('ignores defaults if string contains them all', function() { - var input = "tcp://user1:pass2@host3:3333/databaseName"; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, 'user1'); - assert.equal(output.database, 'databaseName'); - assert.equal(output.port, 3333); - assert.equal(output.host, 'host3'); - assert.equal(output.password, 'pass2'); - }) - }); - - test('unix socket', function() { - test('uses defaults', function() { - var input = "/var/run/postgresql"; - var output = utils.normalizeConnectionInfo(input); - assert.equal(output.user, process.env.USER); - assert.equal(output.host, '/var/run/postgresql'); - assert.equal(output.database, process.env.USER); - assert.equal(output.port, 5432); - }); - - test('uses overridden defaults', function() { - defaults.host = "/var/run/postgresql"; - defaults.user = "boom"; - defaults.password = "yeah"; - defaults.port = 1234; - var output = utils.normalizeConnectionInfo("asdf"); - assert.equal(output.user, "boom"); - assert.equal(output.password, "yeah"); - assert.equal(output.port, 1234); - assert.equal(output.host, "/var/run/postgresql"); - }) - }) - }) - }) -}) - -test('libpq connection string building', function() { - var checkForPart = function(array, part) { - assert.ok(array.indexOf(part) > -1, array.join(" ") + " did not contain " + part); - } - - test('builds simple string', function() { - var config = { - user: 'brian', - password: 'xyz', - port: 888, - host: 'localhost', - database: 'bam' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.isNull(err) - var parts = constring.split(" "); - checkForPart(parts, "user='brian'") - checkForPart(parts, "password='xyz'") - checkForPart(parts, "port='888'") - checkForPart(parts, "hostaddr=127.0.0.1") - checkForPart(parts, "dbname='bam'") - })) - }) - test('builds dns string', function() { - var config = { - user: 'brian', - password: 'asdf', - port: 5432, - host: 'localhost' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.isNull(err); - var parts = constring.split(" "); - checkForPart(parts, "user='brian'") - checkForPart(parts, "hostaddr=127.0.0.1") - })) - }) - - test('error when dns fails', function() { - var config = { - user: 'brian', - password: 'asf', - port: 5432, - host: 'asdlfkjasldfkksfd#!$!!!!..com' - } - utils.buildLibpqConnectionString(config, assert.calls(function(err, constring) { - assert.ok(err); - assert.isNull(constring) - })) - }) - - test('password contains < and/or > characters', function () { - return false; - var sourceConfig = { - user:'brian', - password: 'helloe', - port: 5432, - host: 'localhost', - database: 'postgres' - } - var connectionString = 'pg://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; - var config = utils.parseConnectionString(connectionString); - assert.same(config, sourceConfig); - }); - -}) - test('types are exported', function() { var pg = require(__dirname + '/../../lib/index'); assert.ok(pg.types);