Skip to content

Instantly share code, notes, and snippets.

@puyo
Created September 6, 2016 06:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save puyo/03b03a295d482ccde62f6403f8a11101 to your computer and use it in GitHub Desktop.
Save puyo/03b03a295d482ccde62f6403f8a11101 to your computer and use it in GitHub Desktop.
// Imagine if we had a test framework that did this :...(
const models = require('../models')
const logger = require('../logger')
const Promise = require('bluebird')
const co = require('co')
const isGenFn = require('is-generator').fn
const colors = require('colors/safe')
function transactionStrategy(mocha) {
if (!mocha || mocha._transactionStrategy) {
return
}
const Runnable = mocha.Runnable
const oldRun = Runnable.prototype.run
function newRun(done) {
if (this.type === 'hook') {
return oldRun.bind(this)(done)
}
// We are attempting to run a test function (this.fn)
const self = this
const runPromise = Promise.promisify(oldRun.bind(self))
const title = this.title
// Cope with the test function being a generator (use co.wrap)
const maybeGenFn = this.fn
if (isGenFn(maybeGenFn)) {
this.fn = co.wrap(maybeGenFn)
// Replace `toString` to output the original function contents.
this.fn.toString = function () {
// https://github.com/mochajs/mocha/blob/7493bca76662318183e55294e906a4107433e20e/lib/utils.js#L251
return Function.prototype.toString.call(maybeGenFn)
.replace(/^function *\* *\(.*\)\s*{/, 'function () {')
}
}
// Wrap the test function (this.fn) in a transaction that rolls back
// regardless of the test outcome
const noTxFn = this.fn
this.fn = function(ctx) {
logger.info(colors.magenta("-----> " + title))
//logger.debug('CALLING FN WRAPPER')
return models.sequelize.transaction({autocommit: false})
.then(tx => {
models.Sequelize.cls.set('transaction', tx) // ensures correct use of SAVEPOINT
//logger.debug('IN TX, CALLING ACTUAL TEST FN')
const result = noTxFn.call(self, ctx)
let promise
if (result && typeof result.then === 'function') {
promise = result
} else {
promise = Promise.resolve(result)
}
//logger.debug('EXECUTING TEST PROMISE')
return promise
.then(err => {
//logger.debug("ROLLING BACK")
models.Sequelize.cls.set('transaction', null) // avoids double rollback
return tx.rollback()
.then(_ => logger.info(colors.green("<----- " + title)))
}, err => {
//logger.error("ERROR IN TEST", err.message)
return tx.rollback()
.then(_ => logger.info(colors.red("<----- " + title)))
.thenThrow(err)
})
})
}
oldRun.call(self, done)
}
Runnable.prototype.run = newRun
mocha._transactionStrategy = true
}
const mocha = require('mocha')
transactionStrategy(mocha)
@joshuapaling
Copy link

Don't know if you're still working with sequelize... but if you are, did you ever work things out so your code itself could have transactions, and they'd work fine as nested transactions within the tests using the above transaction strategy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment