From 3aba3794cf7d8749c19081314a875af61efee61e Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 17 Nov 2021 10:02:22 -0600 Subject: [PATCH] Use github actions for CI (#2654) This is the initial port to github actions. Still pending are the SSL and client SSL cert tests which are currently being skipped. But perfect is the enemy of the good here, and having no CI because travis-ci keeps not working is unacceptable. --- .github/workflows/ci.yml | 31 ++ .../pg-query-stream/test/async-iterator.ts | 15 +- .../connection-parameters/creation-tests.js | 433 +++++++++--------- 3 files changed, 264 insertions(+), 215 deletions(-) create mode 100644 .github/workflows/ci.yml 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' + ) + }) + ) +})