diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..13c6c77e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:11 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: ci_db_test + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + strategy: + matrix: + node: ['8', '10', '12', '14', '16', '17'] + name: Node ${{ matrix.node }} + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node }} + cache: yarn + - run: yarn install + # TODO(bmc): get ssl tests working in ci + - run: PGTESTNOSSL=true PGUSER=postgres PGPASSWORD=postgres PGDATABASE=ci_db_test yarn test diff --git a/packages/pg-query-stream/test/async-iterator.ts b/packages/pg-query-stream/test/async-iterator.ts index 06539d12..d47ede16 100644 --- a/packages/pg-query-stream/test/async-iterator.ts +++ b/packages/pg-query-stream/test/async-iterator.ts @@ -88,11 +88,16 @@ if (!process.version.startsWith('v8')) { rows.push(row) break } - for await (const row of stream) { - rows.push(row) - } - for await (const row of stream) { - rows.push(row) + + try { + for await (const row of stream) { + rows.push(row) + } + for await (const row of stream) { + rows.push(row) + } + } catch (e) { + // swallow error - node 17 throws if stream is aborted } assert.strictEqual(rows.length, 1) client.release() diff --git a/packages/pg/test/unit/connection-parameters/creation-tests.js b/packages/pg/test/unit/connection-parameters/creation-tests.js index 633b0eaf..40381e78 100644 --- a/packages/pg/test/unit/connection-parameters/creation-tests.js +++ b/packages/pg/test/unit/connection-parameters/creation-tests.js @@ -1,15 +1,18 @@ 'use strict' -var helper = require('../test-helper') -var assert = require('assert') -var ConnectionParameters = require('../../../lib/connection-parameters') -var defaults = require('../../../lib').defaults +const helper = require('../test-helper') +const assert = require('assert') +const ConnectionParameters = require('../../../lib/connection-parameters') +const defaults = require('../../../lib').defaults +const dns = require('dns') // clear process.env for (var key in process.env) { delete process.env[key] } -test('ConnectionParameters construction', function () { +const suite = new helper.Suite() + +suite.test('ConnectionParameters construction', function () { assert.ok(new ConnectionParameters(), 'with null config') assert.ok(new ConnectionParameters({ user: 'asdf' }), 'with config object') assert.ok(new ConnectionParameters('postgres://localhost/postgres'), 'with connection string') @@ -33,13 +36,13 @@ var compare = function (actual, expected, type) { ) } -test('ConnectionParameters initializing from defaults', function () { +suite.test('ConnectionParameters initializing from defaults', function () { var subject = new ConnectionParameters() compare(subject, defaults, 'defaults') assert.ok(subject.isDomainSocket === false) }) -test('ConnectionParameters initializing from defaults with connectionString set', function () { +suite.test('ConnectionParameters initializing from defaults with connectionString set', function () { var config = { user: 'brians-are-the-best', database: 'scoobysnacks', @@ -62,7 +65,7 @@ test('ConnectionParameters initializing from defaults with connectionString set' compare(subject, config, 'defaults-connectionString') }) -test('ConnectionParameters initializing from config', function () { +suite.test('ConnectionParameters initializing from config', function () { var config = { user: 'brian', database: 'home', @@ -83,7 +86,7 @@ test('ConnectionParameters initializing from config', function () { assert.ok(subject.isDomainSocket === false) }) -test('ConnectionParameters initializing from config and config.connectionString', function () { +suite.test('ConnectionParameters initializing from config and config.connectionString', function () { var subject1 = new ConnectionParameters({ connectionString: 'postgres://test@host/db', }) @@ -105,31 +108,31 @@ test('ConnectionParameters initializing from config and config.connectionString' assert.equal(subject4.ssl, true) }) -test('escape spaces if present', function () { +suite.test('escape spaces if present', function () { var subject = new ConnectionParameters('postgres://localhost/post gres') assert.equal(subject.database, 'post gres') }) -test('do not double escape spaces', function () { +suite.test('do not double escape spaces', function () { var subject = new ConnectionParameters('postgres://localhost/post%20gres') assert.equal(subject.database, 'post gres') }) -test('initializing with unix domain socket', function () { +suite.test('initializing with unix domain socket', function () { var subject = new ConnectionParameters('/var/run/') assert.ok(subject.isDomainSocket) assert.equal(subject.host, '/var/run/') assert.equal(subject.database, defaults.user) }) -test('initializing with unix domain socket and a specific database, the simple way', function () { +suite.test('initializing with unix domain socket and a specific database, the simple way', function () { var subject = new ConnectionParameters('/var/run/ mydb') assert.ok(subject.isDomainSocket) assert.equal(subject.host, '/var/run/') assert.equal(subject.database, 'mydb') }) -test('initializing with unix domain socket, the health way', function () { +suite.test('initializing with unix domain socket, the health way', function () { var subject = new ConnectionParameters('socket:/some path/?db=my[db]&encoding=utf8') assert.ok(subject.isDomainSocket) assert.equal(subject.host, '/some path/') @@ -137,7 +140,7 @@ test('initializing with unix domain socket, the health way', function () { assert.equal(subject.client_encoding, 'utf8') }) -test('initializing with unix domain socket, the escaped health way', function () { +suite.test('initializing with unix domain socket, the escaped health way', function () { var subject = new ConnectionParameters('socket:/some%20path/?db=my%2Bdb&encoding=utf8') assert.ok(subject.isDomainSocket) assert.equal(subject.host, '/some path/') @@ -145,201 +148,211 @@ test('initializing with unix domain socket, the escaped health way', function () assert.equal(subject.client_encoding, 'utf8') }) -test('libpq connection string building', function () { - var checkForPart = function (array, part) { - assert.ok(array.indexOf(part) > -1, array.join(' ') + ' did not contain ' + part) +var checkForPart = function (array, part) { + assert.ok(array.indexOf(part) > -1, array.join(' ') + ' did not contain ' + part) +} + +const getDNSHost = async function (host) { + return new Promise((resolve, reject) => { + dns.lookup(host, (err, addresses) => { + err ? reject(err) : resolve(addresses) + }) + }) +} + +suite.testAsync('builds simple string', async function () { + var config = { + user: 'brian', + password: 'xyz', + port: 888, + host: 'localhost', + database: 'bam', } - - 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(!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(!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: '/tmp/', - } - var subject = new ConnectionParameters(config) - subject.getLibpqConnectionString( - assert.calls(function (err, constring) { - assert(!err) - var parts = constring.split(' ') - checkForPart(parts, "user='brian'") - checkForPart(parts, "host='/tmp/'") - }) - ) - }) - - test('config contains quotes and backslashes', function () { - var config = { - user: 'not\\brian', - password: "bad'chars", - port: 5432, - host: '/tmp/', - } - var subject = new ConnectionParameters(config) - subject.getLibpqConnectionString( - assert.calls(function (err, constring) { - assert(!err) - var parts = constring.split(' ') - checkForPart(parts, "user='not\\\\brian'") - checkForPart(parts, "password='bad\\'chars'") - }) - ) - }) - - test('encoding can be specified by config', function () { - var config = { - client_encoding: 'utf-8', - } - var subject = new ConnectionParameters(config) - subject.getLibpqConnectionString( - assert.calls(function (err, constring) { - assert(!err) - var parts = constring.split(' ') - checkForPart(parts, "client_encoding='utf-8'") - }) - ) - }) - - test('password contains < and/or > characters', function () { - var sourceConfig = { - user: 'brian', - password: 'helloe', - port: 5432, - host: 'localhost', - database: 'postgres', - } - var connectionString = - 'postgres://' + - sourceConfig.user + - ':' + - sourceConfig.password + - '@' + - sourceConfig.host + - ':' + - sourceConfig.port + - '/' + - sourceConfig.database - var subject = new ConnectionParameters(connectionString) - assert.equal(subject.password, sourceConfig.password) - }) - - test('username or password contains weird characters', function () { - var defaults = require('../../../lib/defaults') - defaults.ssl = true - var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000' - var subject = new ConnectionParameters(strang) - assert.equal(subject.user, 'my f%irst name') - assert.equal(subject.password, 'is&%awesome!') - assert.equal(subject.host, 'localhost') - assert.equal(subject.ssl, true) - }) - - test('url is properly encoded', function () { - var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl' - var subject = new ConnectionParameters(encoded) - assert.equal(subject.user, 'bi%na%%ry ') - assert.equal(subject.password, 's@f#') - assert.equal(subject.host, 'localhost') - assert.equal(subject.database, ' u%20rl') - }) - - test('ssl is set on client', function () { - var Client = require('../../../lib/client') - var defaults = require('../../../lib/defaults') - defaults.ssl = true - var c = new Client('postgres://user@password:host/database') - assert(c.ssl, 'Client should have ssl enabled via defaults') - }) - - test('coercing string "true" to boolean', function () { - const subject = new ConnectionParameters({ ssl: 'true' }) - assert.strictEqual(subject.ssl, true) - }) - - test('ssl is set on client', function () { - var sourceConfig = { - user: 'brian', - password: 'helloe', - port: 5432, - host: 'localhost', - database: 'postgres', - ssl: { - sslmode: 'verify-ca', - sslca: '/path/ca.pem', - sslkey: '/path/cert.key', - sslcert: '/path/cert.crt', - sslrootcert: '/path/root.crt', - }, - } - var Client = require('../../../lib/client') - var defaults = require('../../../lib/defaults') - defaults.ssl = true - var c = new ConnectionParameters(sourceConfig) - c.getLibpqConnectionString( - assert.calls(function (err, pgCString) { - assert(!err) - assert.equal( - pgCString.indexOf("sslrootcert='/path/root.crt'") !== -1, - true, - 'libpqConnectionString should contain sslrootcert' - ) - }) - ) + var subject = new ConnectionParameters(config) + const dnsHost = await getDNSHost(config.host) + return new Promise((resolve) => { + subject.getLibpqConnectionString(function (err, constring) { + assert(!err) + var parts = constring.split(' ') + checkForPart(parts, "user='brian'") + checkForPart(parts, "password='xyz'") + checkForPart(parts, "port='888'") + checkForPart(parts, `hostaddr='${dnsHost}'`) + checkForPart(parts, "dbname='bam'") + resolve() + }) }) }) + +suite.test('builds dns string', async function () { + var config = { + user: 'brian', + password: 'asdf', + port: 5432, + host: 'localhost', + } + var subject = new ConnectionParameters(config) + const dnsHost = await getDNSHost(config.host) + return new Promise((resolve) => { + subject.getLibpqConnectionString(function (err, constring) { + assert(!err) + var parts = constring.split(' ') + checkForPart(parts, "user='brian'") + checkForPart(parts, `hostaddr='${dnsHost}'`) + resolve() + }) + }) +}) + +suite.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) + }) + ) +}) + +suite.test('connecting to unix domain socket', function () { + var config = { + user: 'brian', + password: 'asf', + port: 5432, + host: '/tmp/', + } + var subject = new ConnectionParameters(config) + subject.getLibpqConnectionString( + assert.calls(function (err, constring) { + assert(!err) + var parts = constring.split(' ') + checkForPart(parts, "user='brian'") + checkForPart(parts, "host='/tmp/'") + }) + ) +}) + +suite.test('config contains quotes and backslashes', function () { + var config = { + user: 'not\\brian', + password: "bad'chars", + port: 5432, + host: '/tmp/', + } + var subject = new ConnectionParameters(config) + subject.getLibpqConnectionString( + assert.calls(function (err, constring) { + assert(!err) + var parts = constring.split(' ') + checkForPart(parts, "user='not\\\\brian'") + checkForPart(parts, "password='bad\\'chars'") + }) + ) +}) + +suite.test('encoding can be specified by config', function () { + var config = { + client_encoding: 'utf-8', + } + var subject = new ConnectionParameters(config) + subject.getLibpqConnectionString( + assert.calls(function (err, constring) { + assert(!err) + var parts = constring.split(' ') + checkForPart(parts, "client_encoding='utf-8'") + }) + ) +}) + +suite.test('password contains < and/or > characters', function () { + var sourceConfig = { + user: 'brian', + password: 'helloe', + port: 5432, + host: 'localhost', + database: 'postgres', + } + var connectionString = + 'postgres://' + + sourceConfig.user + + ':' + + sourceConfig.password + + '@' + + sourceConfig.host + + ':' + + sourceConfig.port + + '/' + + sourceConfig.database + var subject = new ConnectionParameters(connectionString) + assert.equal(subject.password, sourceConfig.password) +}) + +suite.test('username or password contains weird characters', function () { + var defaults = require('../../../lib/defaults') + defaults.ssl = true + var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000' + var subject = new ConnectionParameters(strang) + assert.equal(subject.user, 'my f%irst name') + assert.equal(subject.password, 'is&%awesome!') + assert.equal(subject.host, 'localhost') + assert.equal(subject.ssl, true) +}) + +suite.test('url is properly encoded', function () { + var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl' + var subject = new ConnectionParameters(encoded) + assert.equal(subject.user, 'bi%na%%ry ') + assert.equal(subject.password, 's@f#') + assert.equal(subject.host, 'localhost') + assert.equal(subject.database, ' u%20rl') +}) + +suite.test('ssl is set on client', function () { + var Client = require('../../../lib/client') + var defaults = require('../../../lib/defaults') + defaults.ssl = true + var c = new Client('postgres://user@password:host/database') + assert(c.ssl, 'Client should have ssl enabled via defaults') +}) + +suite.test('coercing string "true" to boolean', function () { + const subject = new ConnectionParameters({ ssl: 'true' }) + assert.strictEqual(subject.ssl, true) +}) + +suite.test('ssl is set on client', function () { + var sourceConfig = { + user: 'brian', + password: 'helloe', + port: 5432, + host: 'localhost', + database: 'postgres', + ssl: { + sslmode: 'verify-ca', + sslca: '/path/ca.pem', + sslkey: '/path/cert.key', + sslcert: '/path/cert.crt', + sslrootcert: '/path/root.crt', + }, + } + var Client = require('../../../lib/client') + var defaults = require('../../../lib/defaults') + defaults.ssl = true + var c = new ConnectionParameters(sourceConfig) + c.getLibpqConnectionString( + assert.calls(function (err, pgCString) { + assert(!err) + assert.equal( + pgCString.indexOf("sslrootcert='/path/root.crt'") !== -1, + true, + 'libpqConnectionString should contain sslrootcert' + ) + }) + ) +})