knex/test/tape/transactions.js

450 lines
16 KiB
JavaScript
Raw Normal View History

'use strict';
2016-10-12 01:05:08 -04:00
var Promise = require('bluebird')
2015-04-24 15:20:35 -04:00
var harness = require('./harness')
var tape = require('tape')
var JSONStream = require('JSONStream')
module.exports = function(knex) {
tape(knex.client.driverName + ' - transactions: before', function(t) {
knex.schema.dropTableIfExists('test_table')
.createTable('test_table', function(t) {
t.integer('id')
t.string('name')
})
.then(function() {
t.end()
})
})
var test = harness('test_table', knex)
test('transaction', function (t) {
return knex.transaction(function(trx) {
return trx.insert({id: 1, name: 'A'}).into('test_table')
})
.then(function() {
return knex.select('*').from('test_table').then(function(results) {
t.equal(results.length, 1, 'One row inserted')
})
})
})
test('transaction rollback on returned rejected promise', function (t) {
var testError = new Error('Not inserting')
var trxQueryCount = 0
var trxRejected
return knex.transaction(function (trx) {
return trx.insert({id: 1, name: 'A'}).into('test_table').then(function () {
throw testError
})
})
.on('query', function () {
++trxQueryCount
})
.catch(function (err) {
t.equal(err, testError, 'Expected error reported')
trxRejected = true
})
.finally(function () {
// BEGIN, INSERT, ROLLBACK
// oracle & mssql: BEGIN & ROLLBACK not reported as queries
var expectedQueryCount =
knex.client.driverName === 'oracledb' ||
knex.client.driverName === 'mssql' ? 1 : 3
t.equal(trxQueryCount, expectedQueryCount, 'Expected number of transaction SQL queries executed')
t.equal(trxRejected, true, 'Transaction promise rejected')
return knex.select('*').from('test_table').then(function (results) {
t.equal(results.length, 0, 'No rows inserted')
})
})
})
test('transaction rollback on error throw', function (t) {
var testError = new Error('Boo!!!')
var trxQueryCount = 0
var trxRejected
return knex.transaction(function () {
throw testError
})
.on('query', function () {
++trxQueryCount
})
.catch(function (err) {
t.equal(err, testError, 'Expected error reported')
trxRejected = true;
})
.finally(function () {
// BEGIN, ROLLBACK
// oracle & mssql: BEGIN & ROLLBACK not reported as queries
var expectedQueryCount =
knex.client.driverName === 'oracledb' ||
knex.client.driverName === 'mssql' ? 0 : 2
t.equal(trxQueryCount, expectedQueryCount, 'Expected number of transaction SQL queries executed')
t.equal(trxRejected, true, 'Transaction promise rejected')
})
})
test('transaction savepoint rollback on returned rejected promise', function (t) {
var testError = new Error('Rolling Back Savepoint')
var trx1QueryCount = 0
var trx2QueryCount = 0
var trx2Rejected
return knex.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table').then(function () {
// Nested transaction (savepoint)
return trx1.transaction(function (trx2) {
// Insert and then roll back to savepoint
return trx2.table('test_table').insert({id: 2, name: 'B'}).then(function () {
return trx2('test_table').then(function (results) {
t.equal(results.length, 2, 'Two rows inserted')
})
.throw(testError)
})
})
.on('query', function () {
++trx2QueryCount
})
}).catch(function (err) {
t.equal(err, testError, 'Expected error reported')
trx2Rejected = true
})
})
.on('query', function () {
++trx1QueryCount
})
.finally(function () {
// trx1: BEGIN, INSERT, ROLLBACK
// trx2: SAVEPOINT, INSERT, SELECT, ROLLBACK TO SAVEPOINT
// oracle & mssql: BEGIN & ROLLBACK not reported as queries
var expectedTrx1QueryCount =
knex.client.driverName === 'oracledb' ||
knex.client.driverName === 'mssql' ? 1 : 3
var expectedTrx2QueryCount = 4
expectedTrx1QueryCount += expectedTrx2QueryCount
t.equal(trx1QueryCount, expectedTrx1QueryCount, 'Expected number of parent transaction SQL queries executed')
t.equal(trx2QueryCount, expectedTrx2QueryCount, 'Expected number of nested transaction SQL queries executed')
t.equal(trx2Rejected, true, 'Nested transaction promise rejected')
return knex.select('*').from('test_table').then(function (results) {
t.equal(results.length, 1, 'One row inserted')
})
})
})
test('transaction savepoint rollback on error throw', function (t) {
var testError = new Error('Rolling Back Savepoint')
var trx1QueryCount = 0
var trx2QueryCount = 0
var trx2Rejected
return knex.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table').then(function () {
// Nested transaction (savepoint)
return trx1.transaction(function () { // trx2
// Roll back to savepoint
throw testError
})
.on('query', function () {
++trx2QueryCount
})
}).catch(function (err) {
t.equal(err, testError, 'Expected error reported')
trx2Rejected = true
})
})
.on('query', function () {
++trx1QueryCount
})
.finally(function () {
// trx1: BEGIN, INSERT, ROLLBACK
// trx2: SAVEPOINT, ROLLBACK TO SAVEPOINT
// oracle & mssql: BEGIN & ROLLBACK not reported as queries
var expectedTrx1QueryCount =
knex.client.driverName === 'oracledb' ||
knex.client.driverName === 'mssql' ? 1 : 3
var expectedTrx2QueryCount = 2
expectedTrx1QueryCount += expectedTrx2QueryCount
t.equal(trx1QueryCount, expectedTrx1QueryCount, 'Expected number of parent transaction SQL queries executed')
t.equal(trx2QueryCount, expectedTrx2QueryCount, 'Expected number of nested transaction SQL queries executed')
t.equal(trx2Rejected, true, 'Nested transaction promise rejected')
return knex.select('*').from('test_table').then(function (results) {
t.equal(results.length, 1, 'One row inserted')
})
})
})
2016-02-24 14:28:00 +01:00
test('sibling nested transactions - second created after first one commits', function (t) {
var secondTransactionCompleted = false
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
.then(function () {
return trx1.insert({id: 2, name: 'B'}).into('test_table')
})
})
.then(function () {
return trx.transaction(function (trx2) {
return trx2('test_table').then(function (results) {
secondTransactionCompleted = true
t.equal(results.length, 2, 'First sibling transaction committed before starting the second one')
})
})
})
})
.finally(function () {
t.equal(secondTransactionCompleted, true, 'Second sibling transaction completed')
})
})
test('sibling nested transactions - both chained sibling transactions committed', function (t) {
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
})
.then(function () {
return trx.transaction(function (trx2) {
return trx2.insert({id: 2, name: 'B'}).into('test_table')
})
})
})
.finally(function () {
return knex('test_table').then(function (results) {
t.equal(results.length, 2, 'Parent transaction inserted 2 records')
})
})
})
test('sibling nested transactions - second created after first one rolls back by returning a rejected promise', function (t) {
var secondTransactionCompleted = false
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
.then(function () {
throw new Error('test rollback')
})
})
.catch(function (err) {
t.equal(err.message, 'test rollback', 'First sibling transaction rolled back before starting the second one')
return trx.transaction(function (trx2) {
return trx2('test_table').then(function () {
secondTransactionCompleted = true
})
})
})
})
.finally(function () {
t.equal(secondTransactionCompleted, true, 'Second sibling transaction completed')
})
})
test('sibling nested transactions - second commits data after first one rolls back by returning a rejected promise', function (t) {
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
.then(function () {
throw new Error('test rollback')
})
})
.catch(function (err) {
t.equal(err.message, 'test rollback', 'First sibling transaction rolled back before starting the second one')
return trx.transaction(function (trx2) {
return trx2.insert([{id: 2, name: 'B'}, {id: 3, name: 'C'}]).into('test_table')
})
})
})
.finally(function () {
return knex('test_table').then(function (results) {
t.equal(results.length, 2, 'Parent transaction inserted two records')
})
})
})
test('sibling nested transactions - second created after first one rolls back by throwing', function (t) {
var secondTransactionCompleted = false
return knex.transaction(function (trx) {
return trx.transaction(function () {
throw new Error('test rollback')
})
.catch(function (err) {
t.equal(err.message, 'test rollback', 'First sibling transaction rolled back before starting the second one')
return trx.transaction(function (trx2) {
return trx2('test_table').then(function () {
secondTransactionCompleted = true
})
})
})
})
.finally(function () {
t.equal(secondTransactionCompleted, true, 'Second sibling transaction completed')
})
})
test('sibling nested transactions - second commits data after first one rolls back by throwing', function (t) {
return knex.transaction(function (trx) {
return trx.transaction(function () {
throw new Error('test rollback')
})
.catch(function (err) {
t.equal(err.message, 'test rollback', 'First sibling transaction rolled back before starting the second one')
return trx.transaction(function (trx2) {
return trx2.insert([{id: 1, name: 'A'}]).into('test_table')
})
})
})
.finally(function () {
return knex('test_table').then(function (results) {
t.equal(results.length, 1, 'Parent transaction inserted one record')
})
})
})
test('sibling nested transactions - first commits data even though second one rolls back by returning a rejected promise', function (t) {
var secondTransactionCompleted = false
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
})
.then(function () {
return trx.transaction(function (trx2) {
return trx2.insert([{id: 2, name: 'B'}, {id: 3, name: 'C'}]).into('test_table')
.then(function () {
secondTransactionCompleted = true
throw new Error('test rollback')
})
})
.catch(function () {})
})
})
.finally(function () {
t.equal(secondTransactionCompleted, true, 'Second sibling transaction completed')
return knex('test_table').then(function (results) {
t.equal(results.length, 1, 'Parent transaction inserted one record')
})
})
})
test('sibling nested transactions - first commits data even though second one rolls back by throwing', function (t) {
var secondTransactionCompleted = false
return knex.transaction(function (trx) {
return trx.transaction(function (trx1) {
return trx1.insert({id: 1, name: 'A'}).into('test_table')
})
.then(function () {
return trx.transaction(function () {
secondTransactionCompleted = true
throw new Error('test rollback')
})
.catch(function () {})
})
})
.finally(function () {
t.equal(secondTransactionCompleted, true, 'Second sibling transaction completed')
return knex('test_table').then(function (results) {
t.equal(results.length, 1, 'Parent transaction inserted one record')
})
})
})
2015-04-24 15:20:35 -04:00
test('#625 - streams/transactions', 'postgresql', function(t) {
var cid, queryCount = 0;
return knex.transaction(function(tx) {
2016-10-12 01:05:08 -04:00
Promise.each([
2015-04-24 15:20:35 -04:00
'SET join_collapse_limit to 1',
'SET enable_nestloop = off'
2016-10-12 01:05:08 -04:00
], function(request) {
return tx.raw(request)
})
.then(function() {
var stream = tx.table('test_table').stream();
2015-04-24 15:20:35 -04:00
stream.on('end', function () {
tx.commit();
t.equal(queryCount, 5, 'Five queries run')
});
stream.pipe(JSONStream.stringify());
})
2016-10-12 01:05:08 -04:00
.catch(tx.rollback)
2015-04-24 15:20:35 -04:00
})
.on('query', function(q) {
if (!cid) {
cid = q.__knexUid
} else {
if (cid !== q.__knexUid) {
throw new Error('Invalid connection ID')
}
}
queryCount++
})
})
2015-04-28 15:05:17 -04:00
test('#785 - skipping extra transaction statements after commit / rollback', function(t) {
2015-04-28 15:05:17 -04:00
var queryCount = 0
return knex.transaction(function(trx) {
2015-04-28 15:05:17 -04:00
knex('test_table')
.transacting(trx)
.insert({name: 'Inserted before rollback called.'})
.then(function() {
2015-04-28 15:05:17 -04:00
trx.rollback(new Error('Rolled back'));
})
.then(function() {
return knex('test_table')
.transacting(trx)
.insert({name: 'Inserted after rollback called.'})
.then(function(resp) {
2015-04-28 19:11:43 -04:00
t.error(resp)
2015-04-28 15:05:17 -04:00
})
2015-04-28 19:11:43 -04:00
.catch(function() {})
2015-04-28 15:05:17 -04:00
})
})
2015-04-28 19:11:43 -04:00
.on('query', function() {
2015-04-28 15:05:17 -04:00
queryCount++
})
.catch(function(err) {
t.equal(err.message, 'Rolled back', 'Transaction promise rejected with expected error')
2015-04-28 15:05:17 -04:00
})
.finally(function() {
// oracle & mssql: BEGIN & ROLLBACK not reported as queries
var expectedQueryCount =
knex.client.driverName === 'oracledb' ||
knex.client.driverName === 'mssql' ? 1 : 3
t.equal(queryCount, expectedQueryCount, 'Expected number of transaction SQL queries executed')
2015-04-28 15:05:17 -04:00
})
})
test('#805 - nested ddl transaction', function() {
return knex.transaction(function(knex) {
return knex.transaction(function(trx) {
return trx.schema.createTable('ages', function(t) {
t.increments('id').primary();
t.string('name').unique().notNull();
});
})
})
.finally(function() {
return knex.schema.dropTableIfExists('ages')
});
});
if (knex.client.driverName === 'pg') {
// TODO: fix to work without old tables from mocha tests
2015-12-10 15:14:43 -06:00
tape('allows postgres ? operator in knex.raw() if no bindings given #519 and #888', function (t) {
t.plan(1)
knex.from('test_table_two')
.whereRaw("(json_data->'me')::jsonb \\?& array['keyOne', 'keyTwo']")
.where('id', '>', 1)
.then(function (result) {
t.equal(result.length, 0, "Table should have been empty")
return result
})
.finally(function () {
t.end()
});
})
}
}