diff --git a/index.js b/index.js index 3c2e405..bc79d75 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ 'use strict' +const defaultPg = require('pg') const fp = require('fastify-plugin') -var defaultPg = require('pg') function transactionUtil (pool, fn, cb) { pool.connect((err, client, done) => { @@ -9,10 +9,9 @@ function transactionUtil (pool, fn, cb) { const shouldAbort = (err) => { if (err) { - client.query('ROLLBACK', () => { - done() - }) + client.query('ROLLBACK', done) } + return !!err } @@ -21,9 +20,8 @@ function transactionUtil (pool, fn, cb) { client.query('COMMIT', (err) => { done() - if (err) { - return cb(err) - } + if (err) return cb(err) + return cb(null, res) }) } @@ -34,9 +32,7 @@ function transactionUtil (pool, fn, cb) { const promise = fn(client, commit) if (promise && typeof promise.then === 'function') { - promise.then( - (res) => commit(null, res), - (e) => commit(e)) + promise.then((res) => commit(null, res), (e) => commit(e)) } }) }) @@ -49,7 +45,8 @@ function transact (fn, cb) { return new Promise((resolve, reject) => { transactionUtil(this, fn, function (err, res) { - if (err) { return reject(err) } + if (err) return reject(err) + return resolve(res) }) }) @@ -81,26 +78,26 @@ function fastifyPostgres (fastify, options, next) { transact: transact.bind(pool) } + fastify.addHook('onClose', (fastify, done) => pool.end(done)) + if (name) { if (!fastify.pg) { fastify.decorate('pg', {}) } if (fastify.pg[name]) { - return next(new Error('Connection name has already been registered: ' + name)) + return next(new Error(`fastify-postgres '${name}' instance name has already been registered`)) } fastify.pg[name] = db } else { if (fastify.pg) { - next(new Error('fastify-postgres has already registered')) + return next(new Error('fastify-postgres has already been registered')) } else { - fastify.pg = db + fastify.decorate('pg', db) } } - fastify.addHook('onClose', (fastify, done) => pool.end(done)) - next() } diff --git a/package.json b/package.json index 869cba7..3f8ae94 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "description": "Fastify PostgreSQL connection plugin", "main": "index.js", "scripts": { - "test": "standard && tap test.js", - "load-data": "docker run --rm --link fastify-postgres:postgres postgres:11-alpine psql -h postgres -d postgres -c 'CREATE TABLE users(id serial PRIMARY KEY, username VARCHAR (50) NOT NULL);' -U postgres", - "postgres": "docker run --rm -p 5432:5432 --name fastify-postgres postgres:11-alpine" + "test": "standard && tap -J test/*.test.js", + "test:report": "standard && tap -J --coverage-report=html test/*.test.js", + "test:verbose": "standard && tap -J test/*.test.js -Rspec", + "load-data": "docker run --rm -d --link fastify-postgres:postgres postgres:11-alpine psql -h postgres -d postgres -c 'CREATE TABLE users(id serial PRIMARY KEY, username VARCHAR (50) NOT NULL);' -U postgres", + "postgres": "docker run --rm -d -p 5432:5432 --name fastify-postgres postgres:11-alpine" }, "repository": { "type": "git", @@ -27,16 +29,21 @@ }, "homepage": "https://github.com/fastify/fastify-postgres#readme", "dependencies": { - "fastify-plugin": "^1.4.0" + "fastify-plugin": "^1.5.0" }, "devDependencies": { - "fastify": "^2.0.0", + "fastify": "^2.3.0", "pg": "*", "pg-native": "^3.0.0", - "standard": "^12.0.0", - "tap": "^12.5.2" + "standard": "^12.0.1", + "tap": "^12.7.0" }, "peerDependencies": { "pg": ">=6.0.0" + }, + "greenkeeper": { + "ignore": [ + "tap" + ] } } diff --git a/test.js b/test.js deleted file mode 100644 index c34dbcb..0000000 --- a/test.js +++ /dev/null @@ -1,375 +0,0 @@ -'use strict' - -const t = require('tap') -const test = t.test -const Fastify = require('fastify') -const fastifyPostgres = require('./index') - -test('fastify.pg namespace should exist', t => { - t.plan(5) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - t.ok(fastify.pg) - t.ok(fastify.pg.connect) - t.ok(fastify.pg.pool) - t.ok(fastify.pg.Client) - fastify.close() - }) -}) - -test('should be able to connect and perform a query', t => { - t.plan(4) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.connect(onConnect) - }) - - function onConnect (err, client, done) { - t.error(err) - client.query('SELECT NOW()', (err, result) => { - done() - t.error(err) - t.ok(result.rows) - fastify.close() - }) - } -}) - -test('use query util', t => { - t.plan(3) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.query('SELECT NOW()', (err, result) => { - t.error(err) - t.ok(result.rows) - fastify.close() - }) - }) -}) - -test('use query util with promises', t => { - t.plan(2) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg - .query('SELECT NOW()') - .then(result => { - t.ok(result.rows) - fastify.close() - }) - .catch(err => { - t.fail(err) - fastify.close() - }) - }) -}) - -test('use native module', t => { - t.plan(2) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres', - native: true - }) - - fastify.ready(err => { - t.error(err) - fastify.pg - .query('SELECT 1 AS one') - .then(result => { - t.ok(result.rows[0].one === 1) - fastify.close() - }) - .catch(err => { - t.fail(err) - fastify.close() - }) - }) -}) - -test('use alternative pg module', t => { - const altPg = require('pg') - t.plan(2) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - connectionString: 'postgres://postgres@localhost/postgres', - pg: altPg - }) - - fastify.ready(err => { - t.error(err) - fastify.pg - .query('SELECT 1 AS one') - .then(result => { - t.ok(result.rows[0].one === 1) - fastify.close() - }) - .catch(err => { - t.fail(err) - fastify.close() - }) - }) -}) - -test('fastify.pg.test namespace should exist', t => { - t.plan(6) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - t.ok(fastify.pg) - t.ok(fastify.pg.test) - t.ok(fastify.pg.test.connect) - t.ok(fastify.pg.test.pool) - t.ok(fastify.pg.test.Client) - fastify.close() - }) -}) - -test('fastify.pg.test should be able to connect and perform a query', t => { - t.plan(4) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.test.connect(onConnect) - }) - - function onConnect (err, client, done) { - t.error(err) - client.query('SELECT NOW()', (err, result) => { - done() - t.error(err) - t.ok(result.rows) - fastify.close() - }) - } -}) - -test('fastify.pg.test use query util', t => { - t.plan(3) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.test.query('SELECT NOW()', (err, result) => { - t.error(err) - t.ok(result.rows) - fastify.close() - }) - }) -}) - -test('fastify.pg.test use query util with promises', t => { - t.plan(2) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.test - .query('SELECT NOW()') - .then(result => { - t.ok(result.rows) - fastify.close() - }) - .catch(err => { - t.fail(err) - fastify.close() - }) - }) -}) - -test('fastify.pg.test use native module', t => { - t.plan(2) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres', - native: true - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.test - .query('SELECT 1 AS one') - .then(result => { - t.ok(result.rows[0].one === 1) - fastify.close() - }) - .catch(err => { - t.fail(err) - fastify.close() - }) - }) -}) - -test('fastify.pg.test should throw with duplicate connection names', t => { - t.plan(1) - - const fastify = Fastify() - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - .register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.is(err.message, 'Connection name has already been registered: test') - }) -}) - -test('fastify.pg.test use transact util with promise', t => { - t.plan(3) - - const fastify = Fastify() - t.tearDown(fastify.close.bind(fastify)) - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - fastify.pg.test - .transact(client => client.query('INSERT INTO users(username) VALUES($1) RETURNING id', ['with-promise'])) - .then(result => { - t.equals(result.rows.length, 1) - fastify.pg.test - .query(`SELECT * FROM users WHERE username = 'with-promise'`) - .then(result => { - t.ok(result.rows[0].username === 'with-promise') - }).catch(err => { - t.fail(err) - }) - }) - .catch(err => { - t.fail(err) - }) - }) -}) - -test('fastify.pg.test use transact util with callback', t => { - t.plan(4) - - const fastify = Fastify() - t.tearDown(fastify.close.bind(fastify)) - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - - fastify.pg.test - .transact(client => client.query('INSERT INTO users(username) VALUES($1) RETURNING id', ['with-callback']), function (err, res) { - t.error(err) - t.equals(res.rows.length, 1) - - fastify.pg.test - .query(`SELECT * FROM users WHERE username = 'with-callback'`) - .then(result => { - t.ok(result.rows[0].username === 'with-callback') - }).catch(err => { - t.fail(err) - }) - }) - }) -}) - -test('fastify.pg.test use transact util with commit callback', t => { - t.plan(4) - - const fastify = Fastify() - t.tearDown(fastify.close.bind(fastify)) - - fastify.register(fastifyPostgres, { - name: 'test', - connectionString: 'postgres://postgres@localhost/postgres' - }) - - fastify.ready(err => { - t.error(err) - - fastify.pg.test.transact((client, commit) => { - client.query('INSERT INTO users(username) VALUES($1) RETURNING id', ['commit-callback'], (err, id) => { - commit(err, id) - }) - }, function (err, res) { - t.error(err) - t.equals(res.rows.length, 1) - - fastify.pg.test - .query(`SELECT * FROM users WHERE username = 'commit-callback'`) - .then(result => { - t.ok(result.rows[0].username === 'commit-callback') - }).catch(err => { - t.fail(err) - }) - }) - }) -}) diff --git a/test/initialization.test.js b/test/initialization.test.js new file mode 100644 index 0000000..169dc4d --- /dev/null +++ b/test/initialization.test.js @@ -0,0 +1,176 @@ +'use strict' + +const t = require('tap') +const test = t.test +const Fastify = require('fastify') +const fastifyPostgres = require('../index') + +test('Should be able to use native module', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + native: true + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .query('SELECT 1 AS one') + .then((result) => { + t.is(result.rows[0].one, 1) + }) + .catch((err) => { + t.fail(err) + }) + }) +}) + +test('Should be able to use an alternative pg module', (t) => { + t.plan(2) + + const altPg = require('pg') + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + pg: altPg + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .query('SELECT 1 AS one') + .then((result) => { + t.is(result.rows[0].one, 1) + }) + .catch((err) => { + t.fail(err) + }) + }) +}) + +test('Should not throw if registered within different scopes (with and without named instances)', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(function scopeOne (instance, opts, next) { + instance.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + next() + }) + + fastify.register(function scopeTwo (instance, opts, next) { + instance.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'one' + }) + + instance.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'two' + }) + + next() + }) + + fastify.ready((err) => { + t.error(err) + t.is(err, null) + }) +}) + +test('Should throw when trying to register multiple instances without giving a name', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.ok(err) + t.is(err.message, 'fastify-postgres has already been registered') + }) +}) + +test('Should throw when trying to register duplicate connection names', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + const name = 'test' + + fastify + .register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name + }) + .register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name + }) + + fastify.ready((err) => { + t.ok(err) + t.is(err.message, `fastify-postgres '${name}' instance name has already been registered`) + }) +}) + +test('fastify.pg namespace should exist', (t) => { + t.plan(5) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + t.ok(fastify.pg) + t.ok(fastify.pg.connect) + t.ok(fastify.pg.pool) + t.ok(fastify.pg.Client) + }) +}) + +test('fastify.pg.test namespace should exist', (t) => { + t.plan(6) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + t.ok(fastify.pg) + t.ok(fastify.pg.test) + t.ok(fastify.pg.test.connect) + t.ok(fastify.pg.test.pool) + t.ok(fastify.pg.test.Client) + }) +}) diff --git a/test/query.test.js b/test/query.test.js new file mode 100644 index 0000000..5af59b4 --- /dev/null +++ b/test/query.test.js @@ -0,0 +1,263 @@ +'use strict' + +const t = require('tap') +const test = t.test +const Fastify = require('fastify') +const fastifyPostgres = require('../index') + +test('When fastify.pg root namespace is used:', (t) => { + t.test('Should be able to connect and perform a query with a callback', (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.connect(onConnect) + }) + + function onConnect (err, client, done) { + t.error(err) + + client.query('SELECT NOW()', (err, result) => { + done() + t.error(err) + t.ok(result.rows) + }) + } + }) + + t.test('Should be able to use the query util with a callback', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.query('SELECT NOW()', (err, result) => { + t.error(err) + t.ok(result.rows) + }) + }) + }) + + t.test('Should be able to use the query util with promises', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .query('SELECT NOW()') + .then((result) => { + t.ok(result.rows) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + + t.test( + 'query util should return an error when pg fails to perform an operation using a callback', + (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + const DB_NAME = 'database_that_do_not_exist' + + fastify.register(fastifyPostgres, { + connectionString: `postgres://postgres@localhost/${DB_NAME}` + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.query('SELECT NOW()', (err, result) => { + t.is(result, undefined) + t.ok(err) + t.is(err.message, `database "${DB_NAME}" does not exist`) + }) + }) + } + ) + + t.test('Should throw when pg fails to perform operation with promises', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + const DB_NAME = 'database_that_do_not_exist' + + fastify.register(fastifyPostgres, { + connectionString: `postgres://postgres@localhost/${DB_NAME}` + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .query('SELECT NOW()') + .then((result) => { + t.fail(result) + }) + .catch((err) => { + t.ok(err) + t.is(err.message, `database "${DB_NAME}" does not exist`) + }) + }) + }) + + t.end() +}) + +test('When fastify.pg.test namespace is used:', (t) => { + t.test('Should be able to connect and perform a query', (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + fastify.pg.test.connect(onConnect) + }) + + function onConnect (err, client, done) { + t.error(err) + client.query('SELECT NOW()', (err, result) => { + done() + t.error(err) + t.ok(result.rows) + }) + } + }) + + t.test('Should be able to use query util with a callback', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + fastify.pg.test.query('SELECT NOW()', (err, result) => { + t.error(err) + t.ok(result.rows) + }) + }) + }) + + t.test('Should be able to use query util with promises', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test + .query('SELECT NOW()') + .then((result) => { + t.ok(result.rows) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + + t.test('Should be able to use native module', (t) => { + t.plan(2) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test', + native: true + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test + .query('SELECT 1 AS one') + .then((result) => { + t.is(result.rows[0].one, 1) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + + t.test('Should throw when pg fails to perform an operation with promises', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + const DB_NAME = 'database_that_do_not_exist' + + fastify.register(fastifyPostgres, { + connectionString: `postgres://postgres@localhost/${DB_NAME}`, + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test + .query('SELECT NOW()') + .then((result) => { + t.fail(result) + }) + .catch((err) => { + t.ok(err) + t.is(err.message, `database "${DB_NAME}" does not exist`) + }) + }) + }) + + t.end() +}) diff --git a/test/transaction.test.js b/test/transaction.test.js new file mode 100644 index 0000000..2646f52 --- /dev/null +++ b/test/transaction.test.js @@ -0,0 +1,507 @@ +'use strict' + +const t = require('tap') +const test = t.test +const Fastify = require('fastify') +const fastifyPostgres = require('../index') + +test('When fastify.pg root namespace is used:', (t) => { + t.test('Should be able to use transact util with a callback', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.transact( + (client) => + client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'root-with-callback' + ]), + function (err, result) { + t.error(err) + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'root-with-callback') + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should be able to use transact util with promises', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .transact((client) => + client.query('INSERT INTO users(username) VALUES($1) RETURNING id', ['root-with-promise']) + ) + .then((result) => { + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'root-with-promise') + }) + .catch((err) => { + t.fail(err) + }) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + + t.test('Should be able to use transact util with a commit callback', (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.transact( + (client, commit) => + client.query( + 'INSERT INTO users(username) VALUES($1) RETURNING id', + ['root-commit-callback'], + (err, id) => { + commit(err, id) + } + ), + function (err, result) { + t.error(err) + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'root-commit-callback') + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should trigger a rollback when something goes wrong (with callback)', (t) => { + t.plan(9) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.transact( + (client) => { + return client + .query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'root-rollback-user-callback' + ]) + .then((result) => { + t.ok(result) + + const userId = result.rows[0].id + + return client + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.ok(result) + t.is(result.rows[0].username, 'root-rollback-user-callback') + }) + .then(() => { + throw new Error('We make it throw on purpose to trigger a rollback') + }) + }) + }, + function (err, result) { + t.ok(err) + t.is(err.message, 'We make it throw on purpose to trigger a rollback') + t.is(result, undefined) + + fastify.pg + .query(`SELECT * FROM users WHERE username = 'root-rollback-user-callback'`) + .then((result) => { + t.ok(result) + t.is(result.rows.length, 0) + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should trigger a rollback when something goes wrong (with promises)', (t) => { + t.plan(8) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg + .transact((client) => + client + .query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'root-rollback-user-promise' + ]) + .then((result) => { + t.ok(result) + + const userId = result.rows[0].id + + return client + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.ok(result) + t.is(result.rows[0].username, 'root-rollback-user-promise') + }) + .then(() => { + throw new Error('We make it throw on purpose to trigger a rollback') + }) + }) + ) + .catch((err) => { + t.ok(err) + t.is(err.message, 'We make it throw on purpose to trigger a rollback') + + fastify.pg + .query(`SELECT * FROM users WHERE username = 'root-rollback-user-promise'`) + .then((result) => { + t.ok(result) + t.is(result.rows.length, 0) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + }) + + t.test('Should throw if the pool connection throws an error', (t) => { + t.plan(3) + + const fastify = Fastify() + const BAD_DB_NAME = 'db_that_does_not_exist' + + fastify.register(fastifyPostgres, { + connectionString: `postgres://postgres@localhost/${BAD_DB_NAME}` + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.transact((client) => client.query('SELECT NOW()')) + .catch((err) => { + t.ok(err) + t.is(err.message, `database "${BAD_DB_NAME}" does not exist`) + }) + }) + }) + + t.end() +}) + +test('When fastify.pg.test namespace is used:', (t) => { + t.test('Should be able to use transact util with a callback', (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test.transact( + (client) => + client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'namespace-with-callback' + ]), + function (err, result) { + t.error(err) + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg.test + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'namespace-with-callback') + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should be able to use transact util with promises', (t) => { + t.plan(3) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test + .transact((client) => + client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'namespace-with-promise' + ]) + ) + .then((result) => { + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg.test + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'namespace-with-promise') + }) + .catch((err) => { + t.fail(err) + }) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + + t.test('Should be able to use transact util with a commit callback', (t) => { + t.plan(4) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test.transact( + (client, commit) => { + client.query( + 'INSERT INTO users(username) VALUES($1) RETURNING id', + ['namespace-commit-callback'], + (err, id) => { + commit(err, id) + } + ) + }, + function (err, result) { + t.error(err) + t.is(result.rows.length, 1) + + const userId = result.rows[0].id + + fastify.pg.test + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.is(result.rows[0].username, 'namespace-commit-callback') + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should trigger a rollback when something goes wrong (with callback)', (t) => { + t.plan(9) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test.transact( + (client) => { + return client + .query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'namespace-rollback-user-callback' + ]) + .then((result) => { + t.ok(result) + + const userId = result.rows[0].id + + return client + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.ok(result) + t.is(result.rows[0].username, 'namespace-rollback-user-callback') + }) + .then(() => { + throw new Error('We make it throw on purpose to trigger a rollback') + }) + }) + }, + function (err, result) { + t.ok(err) + t.is(err.message, 'We make it throw on purpose to trigger a rollback') + t.is(result, undefined) + + fastify.pg.test + .query(`SELECT * FROM users WHERE username = 'namespace-rollback-user-callback'`) + .then((result) => { + t.ok(result) + t.is(result.rows.length, 0) + }) + .catch((err) => { + t.fail(err) + }) + } + ) + }) + }) + + t.test('Should trigger a rollback when something goes wrong (with promises)', (t) => { + t.plan(8) + + const fastify = Fastify() + t.teardown(() => fastify.close()) + + fastify.register(fastifyPostgres, { + connectionString: 'postgres://postgres@localhost/postgres', + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test + .transact((client) => + client + .query('INSERT INTO users(username) VALUES($1) RETURNING id', [ + 'namespace-rollback-user-promise' + ]) + .then((result) => { + t.ok(result) + + const userId = result.rows[0].id + + return client + .query('SELECT * FROM users WHERE id = $1', [userId]) + .then((result) => { + t.ok(result) + t.is(result.rows[0].username, 'namespace-rollback-user-promise') + }) + .then(() => { + throw new Error('We make it throw on purpose to trigger a rollback') + }) + }) + ) + .catch((err) => { + t.ok(err) + t.is(err.message, 'We make it throw on purpose to trigger a rollback') + + fastify.pg.test + .query(`SELECT * FROM users WHERE username = 'namespace-rollback-user-promise'`) + .then((result) => { + t.ok(result) + t.is(result.rows.length, 0) + }) + .catch((err) => { + t.fail(err) + }) + }) + }) + }) + + t.test('Should throw if the pool connection throws an error', (t) => { + t.plan(3) + + const fastify = Fastify() + const BAD_DB_NAME = 'db_that_does_not_exist' + + fastify.register(fastifyPostgres, { + connectionString: `postgres://postgres@localhost/${BAD_DB_NAME}`, + name: 'test' + }) + + fastify.ready((err) => { + t.error(err) + + fastify.pg.test.transact((client) => client.query('SELECT NOW()')) + .catch((err) => { + t.ok(err) + t.is(err.message, `database "${BAD_DB_NAME}" does not exist`) + }) + }) + }) + + t.end() +})