不要怂,就是干,撸起袖子干!

Commit 16d175a1 by Matt Broadstone

Merge pull request #3359 from sequelize/test-verify-no-running-queries

feat(test/misc): utils for verifying sequelize has no running/pending queries
2 parents ce2e86b1 9920fee5
......@@ -17,15 +17,15 @@ else
test:
make jshint && make teaser && make test-unit && make test-integration
@if [ "$$GREP" ]; then \
make jshint && make teaser && ./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 10000 --reporter $(REPORTER) -g "$$GREP" $(TESTS); \
make jshint && make teaser && ./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 15000 --reporter $(REPORTER) -g "$$GREP" $(TESTS); \
else \
make jshint && make teaser && ./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 10000 --reporter $(REPORTER) $(TESTS); \
make jshint && make teaser && ./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 15000 --reporter $(REPORTER) $(TESTS); \
fi
endif
# Unit tests
test-unit:
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 10000 --reporter $(REPORTER) ./test/unit/*.js ./test/unit/**/*.js
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 15000 --reporter $(REPORTER) ./test/unit/*.js ./test/unit/**/*.js
test-unit-all: test-unit-sqlite test-unit-mysql test-unit-postgres test-unit-postgres-native test-unit-mariadb test-unit-mssql
......@@ -45,9 +45,9 @@ test-unit-postgres-native:
# Integration tests
test-integration:
@if [ "$$GREP" ]; then \
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 10000 --reporter $(REPORTER) -g "$$GREP" $(TESTS); \
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 15000 --reporter $(REPORTER) -g "$$GREP" $(TESTS); \
else \
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 10000 --reporter $(REPORTER) $(TESTS); \
./node_modules/mocha/bin/mocha --globals setImmediate,clearImmediate --ui tdd --check-leaks --colors -t 15000 --reporter $(REPORTER) $(TESTS); \
fi
test-integration-all: test-integration-sqlite test-integration-mysql test-integration-postgres test-integration-postgres-native test-integration-mariadb test-integration-mssql
......@@ -85,7 +85,7 @@ postgres-native:
# Coverage
cover:
rm -rf coverage \
make teaser && ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- -t 10000 --ui tdd $(TESTS); \
make teaser && ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- -t 15000 --ui tdd $(TESTS); \
mariadb-cover:
rm -rf coverage
......
......@@ -612,7 +612,6 @@ module.exports = (function() {
QueryInterface.prototype.delete = function(dao, tableName, identifier, options) {
var self = this
, restrict = false
, cascades = []
, sql = self.QueryGenerator.deleteQuery(tableName, identifier, null, dao.Model);
......@@ -623,70 +622,22 @@ module.exports = (function() {
for (var i = 0; i < length; i++) {
if (dao.Model.associations[keys[i]].options && dao.Model.associations[keys[i]].options.onDelete) {
if (dao.Model.associations[keys[i]].options.onDelete === 'restrict') {
restrict = true;
}
else if (dao.Model.associations[keys[i]].options.onDelete === 'cascade' && dao.Model.associations[keys[i]].options.useHooks === true) {
cascades[cascades.length] = dao.Model.associations[keys[i]].accessors.get;
if (dao.Model.associations[keys[i]].options.onDelete === 'cascade' && dao.Model.associations[keys[i]].options.useHooks === true) {
cascades.push(dao.Model.associations[keys[i]].accessors.get);
}
}
}
}
return new Utils.Promise(function(resolve, reject) {
var tick = 0;
var iterate = function(err, i) {
if (err) {
return reject(err);
}
if (i >= cascades.length) {
return resolve();
}
dao[cascades[i]]().success(function(tasks) {
if (tasks === null || tasks.length < 1) {
if (i >= cascades.length) {
return resolve();
} else {
tick++;
return iterate(null, tick);
}
}
tasks = Array.isArray(tasks) ? tasks : [tasks];
var ii = 0;
var next = function(err, ii) {
if (!!err || ii >= tasks.length) {
return iterate(err);
}
tasks[ii].destroy().error(function(err) {
return iterate(err);
})
.success(function() {
ii++;
if (ii >= tasks.length) {
tick++;
return iterate(null, tick);
}
next(null, ii);
});
};
next(null, ii);
});
};
if (cascades.length > 0) {
iterate(null, tick);
} else {
resolve();
}
}).then(function() {
return Promise.reduce(cascades, function (memo, cascade) {
return dao[cascade]().then(function (instances) {
if (!Array.isArray(instances)) instances = [instances];
return Promise.reduce(instances, function (memo, instance) {
return instance.destroy();
}, []);
});
}, []).then(function () {
return self.sequelize.query(sql, dao, options);
});
};
......
......@@ -198,6 +198,17 @@ module.exports = (function() {
this.importCache = {};
this.test = {
$trackRunningQueries: false,
$runningQueries: 0,
trackRunningQueries: function() {
this.$trackRunningQueries = true;
},
verifyNoRunningQueries: function() {
if (this.$runningQueries > 0) throw new Error('Expected 0 running queries. '+this.$runningQueries+' queries still running');
}
};
Sequelize.runHooks('afterInit', this);
};
......@@ -731,6 +742,10 @@ module.exports = (function() {
return Promise.reject(options.transaction.finished+' has been called on this transaction, you can no longer use it');
}
if (this.test.$trackRunningQueries) {
this.test.$runningQueries++;
}
return Promise.resolve(
options.transaction ? options.transaction.connection : self.connectionManager.getConnection(options)
).then(function (connection) {
......@@ -739,6 +754,10 @@ module.exports = (function() {
if (options.transaction) return;
return self.connectionManager.releaseConnection(connection);
});
}).finally(function () {
if (self.test.$trackRunningQueries) {
self.test.$runningQueries--;
}
});
};
......
......@@ -4676,12 +4676,13 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
});
});
it('with errors', function(done) {
it('with errors', function() {
var self = this
, beforeProject = false
, afterProject = false
, beforeTask = false
, afterTask = false;
, afterTask = false
, VeryCustomError = function() {};
this.Projects.beforeCreate(function(project, options, fn) {
beforeProject = true;
......@@ -4695,7 +4696,7 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
this.Tasks.beforeDestroy(function(task, options, fn) {
beforeTask = true;
fn(new Error('Whoops!'));
fn(new VeryCustomError('Whoops!'));
});
this.Tasks.afterDestroy(function(task, options, fn) {
......@@ -4703,16 +4704,14 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
fn();
});
this.Projects.create({title: 'New Project'}).success(function(project) {
self.Tasks.create({title: 'New Task'}).success(function(task) {
project.setTask(task).success(function() {
project.destroy().error(function(err) {
expect(err).to.be.instanceOf(Error);
return this.Projects.create({title: 'New Project'}).then(function(project) {
return self.Tasks.create({title: 'New Task'}).then(function(task) {
return project.setTask(task).then(function() {
return expect(project.destroy()).to.eventually.be.rejectedWith(VeryCustomError).then(function () {
expect(beforeProject).to.be.true;
expect(afterProject).to.be.true;
expect(beforeTask).to.be.true;
expect(afterTask).to.be.false;
done();
});
});
});
......@@ -5558,9 +5557,9 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
this.MiniTasks.create({mini_title: 'New MiniTask'})
]).bind(this).spread(function(project, task, minitask) {
return this.sequelize.Promise.all([
task.addMiniTask(minitask),
project.addTask(task)
]).return(project);
task.addMiniTask(minitask),
project.addTask(task)
]).return(project);
}).then(function(project) {
return project.destroy();
}).then(function() {
......@@ -5580,37 +5579,32 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
, beforeTask = false
, afterTask = false
, beforeMiniTask = false
, afterMiniTask = false;
, afterMiniTask = false
, VeryCustomError = function() {};
this.Projects.beforeCreate(function(project, options, fn) {
this.Projects.beforeCreate(function(project, options) {
beforeProject = true;
fn();
});
this.Projects.afterCreate(function(project, options, fn) {
this.Projects.afterCreate(function(project, options) {
afterProject = true;
fn();
});
this.Tasks.beforeDestroy(function(task, options, fn) {
this.Tasks.beforeDestroy(function(task, options) {
beforeTask = true;
fn(new Error('Whoops!'));
fn();
throw new VeryCustomError('Whoops!');
});
this.Tasks.afterDestroy(function(task, options, fn) {
this.Tasks.afterDestroy(function(task, options) {
afterTask = true;
fn();
});
this.MiniTasks.beforeDestroy(function(minitask, options, fn) {
this.MiniTasks.beforeDestroy(function(minitask, options) {
beforeMiniTask = true;
});
this.MiniTasks.afterDestroy(function(minitask, options, fn) {
this.MiniTasks.afterDestroy(function(minitask, options) {
afterMiniTask = true;
fn();
});
return this.sequelize.Promise.all([
......@@ -5619,18 +5613,18 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
this.MiniTasks.create({mini_title: 'New MiniTask'})
]).bind(this).spread(function(project, task, minitask) {
return this.sequelize.Promise.all([
task.addMiniTask(minitask),
project.addTask(task)
]).return(project);
task.addMiniTask(minitask),
project.addTask(task)
]).return(project);
}).then(function(project) {
return project.destroy();
}).catch(function() {
expect(beforeProject).to.be.true;
expect(afterProject).to.be.true;
expect(beforeTask).to.be.true;
expect(afterTask).to.be.false;
expect(beforeMiniTask).to.be.false;
expect(afterMiniTask).to.be.false;
return expect(project.destroy()).to.eventually.be.rejectedWith(VeryCustomError).then(function () {
expect(beforeProject).to.be.true;
expect(afterProject).to.be.true;
expect(beforeTask).to.be.true;
expect(afterTask).to.be.false;
expect(beforeMiniTask).to.be.false;
expect(afterMiniTask).to.be.false;
});
});
});
});
......
......@@ -7,7 +7,8 @@ var chai = require('chai')
, DataTypes = require(__dirname + '/../../../lib/data-types')
, datetime = require('chai-datetime')
, async = require('async')
, Promise = Sequelize.Promise;
, Promise = Sequelize.Promise
, _ = require('lodash');
chai.use(datetime);
chai.config.includeStack = true;
......@@ -122,106 +123,79 @@ describe(Support.getTestDialectTeaser('Include'), function() {
return Tag.findAll();
})
}).then(function (results) {
var count = 4
, i = -1
, groups = results.groups
var groups = results.groups
, ranks = results.ranks
, tags = results.tags
, companies = results.companies;
return new Promise(function (resolve, reject) {
async.whilst(
function() { return i < count; },
function(callback) {
i++;
async.auto({
user: function(callback) {
User.create().nodeify(callback);
},
memberships: ['user', function(callback, results) {
var groupMembers = [
{AccUserId: results.user.id, GroupId: groups[0].id, RankId: ranks[0].id},
{AccUserId: results.user.id, GroupId: groups[1].id, RankId: ranks[2].id}
];
if (i < 3) {
groupMembers.push({AccUserId: results.user.id, GroupId: groups[2].id, RankId: ranks[1].id});
}
GroupMember.bulkCreate(groupMembers).nodeify(callback);
}],
products: function(callback) {
Product.bulkCreate([
{title: 'Chair'},
{title: 'Desk'},
{title: 'Bed'},
{title: 'Pen'},
{title: 'Monitor'}
]).done(function(err) {
if (err) return callback(err);
Product.findAll().nodeify(callback);
});
},
userProducts: ['user', 'products', function(callback, results) {
results.user.setProducts([
results.products[(i * 5) + 0],
results.products[(i * 5) + 1],
results.products[(i * 5) + 3]
]).nodeify(callback);
}],
productTags: ['products', function(callback, results) {
var chainer = new Sequelize.Utils.QueryChainer();
chainer.add(results.products[(i * 5) + 0].setTags([
tags[0],
tags[2]
]));
chainer.add(results.products[(i * 5) + 1].setTags([
tags[1]
]));
chainer.add(results.products[(i * 5) + 0].setCategory(tags[1]));
chainer.add(results.products[(i * 5) + 2].setTags([
tags[0]
]));
chainer.add(results.products[(i * 5) + 3].setTags([
tags[0]
]));
chainer.run().done(callback);
}],
companies: ['products', function(callback, results) {
var chainer = new Sequelize.Utils.QueryChainer();
results.products[(i * 5) + 0].setCompany(companies[4]);
results.products[(i * 5) + 1].setCompany(companies[3]);
results.products[(i * 5) + 2].setCompany(companies[2]);
results.products[(i * 5) + 3].setCompany(companies[1]);
results.products[(i * 5) + 4].setCompany(companies[0]);
chainer.run().done(callback);
}],
prices: ['products', function(callback, results) {
Price.bulkCreate([
{ProductId: results.products[(i * 5) + 0].id, value: 5},
{ProductId: results.products[(i * 5) + 0].id, value: 10},
{ProductId: results.products[(i * 5) + 1].id, value: 5},
{ProductId: results.products[(i * 5) + 1].id, value: 10},
{ProductId: results.products[(i * 5) + 1].id, value: 15},
{ProductId: results.products[(i * 5) + 1].id, value: 20},
{ProductId: results.products[(i * 5) + 2].id, value: 20},
{ProductId: results.products[(i * 5) + 3].id, value: 20}
]).nodeify(callback);
}]
}, callback);
},
function(err) {
if (err) return reject(err);
resolve();
return Promise.reduce(_.range(5), function (memo, i) {
return Promise.props({
user: User.create(),
products: Product.bulkCreate([
{title: 'Chair'},
{title: 'Desk'},
{title: 'Bed'},
{title: 'Pen'},
{title: 'Monitor'}
]).then(function(err) {
return Product.findAll();
})
}).then(function (results) {
var user = results.user
, products = results.products
, groupMembers;
groupMembers = [
{AccUserId: user.id, GroupId: groups[0].id, RankId: ranks[0].id},
{AccUserId: user.id, GroupId: groups[1].id, RankId: ranks[2].id}
];
if (i < 3) {
groupMembers.push({AccUserId: user.id, GroupId: groups[2].id, RankId: ranks[1].id});
}
);
});
return Promise.join(
GroupMember.bulkCreate(groupMembers),
user.setProducts([
products[(i * 5) + 0],
products[(i * 5) + 1],
products[(i * 5) + 3]
]),
Promise.join(
products[(i * 5) + 0].setTags([
tags[0],
tags[2]
]),
products[(i * 5) + 1].setTags([
tags[1]
]),
products[(i * 5) + 0].setCategory(tags[1]),
products[(i * 5) + 2].setTags([
tags[0]
]),
products[(i * 5) + 3].setTags([
tags[0]
])
),
Promise.join(
products[(i * 5) + 0].setCompany(companies[4]),
products[(i * 5) + 1].setCompany(companies[3]),
products[(i * 5) + 2].setCompany(companies[2]),
products[(i * 5) + 3].setCompany(companies[1]),
products[(i * 5) + 4].setCompany(companies[0])
),
Price.bulkCreate([
{ProductId: products[(i * 5) + 0].id, value: 5},
{ProductId: products[(i * 5) + 0].id, value: 10},
{ProductId: products[(i * 5) + 1].id, value: 5},
{ProductId: products[(i * 5) + 1].id, value: 10},
{ProductId: products[(i * 5) + 1].id, value: 15},
{ProductId: products[(i * 5) + 1].id, value: 20},
{ProductId: products[(i * 5) + 2].id, value: 20},
{ProductId: products[(i * 5) + 3].id, value: 20}
])
);
});
}, []);
});
});
};
......@@ -1046,7 +1020,7 @@ describe(Support.getTestDialectTeaser('Include'), function() {
return Promise.join(
results.users[0].setGroup(results.groups[1]),
results.users[1].setGroup(results.groups[0]),
results.groups.map(function (group) {
Promise.map(results.groups, function (group) {
return group.setCategories(results.categories);
})
);
......@@ -1099,7 +1073,7 @@ describe(Support.getTestDialectTeaser('Include'), function() {
return Promise.join(
results.users[0].setTeam(results.groups[1]),
results.users[1].setTeam(results.groups[0]),
results.groups.map(function (group) {
Promise.map(results.groups, function (group) {
return group.setTags(results.categories);
})
);
......@@ -1152,7 +1126,7 @@ describe(Support.getTestDialectTeaser('Include'), function() {
return Promise.join(
results.users[0].setGroup(results.groups[1]),
results.users[1].setGroup(results.groups[0]),
results.groups.map(function (group) {
Promise.map(results.groups, function (group) {
return group.setCategories(results.categories);
})
);
......@@ -1253,7 +1227,7 @@ describe(Support.getTestDialectTeaser('Include'), function() {
results.products[2].addTag(results.tags[2], {priority: 2})
);
}).then(function () {
Product.findAll({
return Product.findAll({
include: [
{model: Tag, where: {name: 'C'}}
]
......@@ -1632,8 +1606,7 @@ describe(Support.getTestDialectTeaser('Include'), function() {
return Member.bulkCreate(members).then(function () {
return Album.bulkCreate(albums).then(function () {
Member.findAll({
return Member.findAll({
attributes: ['email'],
include: [
{
......
......@@ -7,7 +7,8 @@ var chai = require('chai')
, DataTypes = require(__dirname + '/../../../lib/data-types')
, datetime = require('chai-datetime')
, async = require('async')
, Promise = Sequelize.Promise;
, Promise = Sequelize.Promise
, _ = require('lodash');
chai.use(datetime);
chai.config.includeStack = true;
......@@ -126,107 +127,80 @@ describe(Support.getTestDialectTeaser('Includes with schemas'), function() {
return Tag.findAll();
})
}).then(function (results) {
var count = 4
, i = -1
, groups = results.groups
, ranks = results.ranks
, tags = results.tags
, companies = results.companies;
return new Promise(function (resolve, reject) {
async.whilst(
function() { return i < count; },
function(callback) {
i++;
async.auto({
user: function(callback) {
AccUser.create().done(callback);
},
memberships: ['user', function(callback, results) {
var groupMembers = [
{AccUserId: results.user.id, GroupId: groups[0].id, RankId: ranks[0].id},
{AccUserId: results.user.id, GroupId: groups[1].id, RankId: ranks[2].id}
];
if (i < 3) {
groupMembers.push({AccUserId: results.user.id, GroupId: groups[2].id, RankId: ranks[1].id});
}
GroupMember.bulkCreate(groupMembers).done(callback);
}],
products: function(callback) {
Product.bulkCreate([
{title: 'Chair'},
{title: 'Desk'},
{title: 'Bed'},
{title: 'Pen'},
{title: 'Monitor'}
]).done(function(err) {
if (err) return callback(err);
Product.findAll().done(callback);
});
},
userProducts: ['user', 'products', function(callback, results) {
results.user.setProducts([
results.products[(i * 5) + 0],
results.products[(i * 5) + 1],
results.products[(i * 5) + 3]
]).done(callback);
}],
productTags: ['products', function(callback, results) {
var chainer = new Sequelize.Utils.QueryChainer();
chainer.add(results.products[(i * 5) + 0].setTags([
tags[0],
tags[2]
]));
chainer.add(results.products[(i * 5) + 1].setTags([
tags[1]
]));
chainer.add(results.products[(i * 5) + 0].setCategory(tags[1]));
chainer.add(results.products[(i * 5) + 2].setTags([
tags[0]
]));
chainer.add(results.products[(i * 5) + 3].setTags([
tags[0]
]));
chainer.run().done(callback);
}],
companies: ['products', function(callback, results) {
var chainer = new Sequelize.Utils.QueryChainer();
results.products[(i * 5) + 0].setCompany(companies[4]);
results.products[(i * 5) + 1].setCompany(companies[3]);
results.products[(i * 5) + 2].setCompany(companies[2]);
results.products[(i * 5) + 3].setCompany(companies[1]);
results.products[(i * 5) + 4].setCompany(companies[0]);
chainer.run().done(callback);
}],
prices: ['products', function(callback, results) {
Price.bulkCreate([
{ProductId: results.products[(i * 5) + 0].id, value: 5},
{ProductId: results.products[(i * 5) + 0].id, value: 10},
{ProductId: results.products[(i * 5) + 1].id, value: 5},
{ProductId: results.products[(i * 5) + 1].id, value: 10},
{ProductId: results.products[(i * 5) + 1].id, value: 15},
{ProductId: results.products[(i * 5) + 1].id, value: 20},
{ProductId: results.products[(i * 5) + 2].id, value: 20},
{ProductId: results.products[(i * 5) + 3].id, value: 20}
]).done(callback);
}]
}, callback);
},
function(err) {
if (err) return reject(err);
resolve();
}
);
});
var groups = results.groups
, ranks = results.ranks
, tags = results.tags
, companies = results.companies;
return Promise.reduce(_.range(5), function (memo, i) {
return Promise.props({
user: AccUser.create(),
products: Product.bulkCreate([
{title: 'Chair'},
{title: 'Desk'},
{title: 'Bed'},
{title: 'Pen'},
{title: 'Monitor'}
]).then(function(err) {
return Product.findAll();
})
}).then(function (results) {
var user = results.user
, products = results.products
, groupMembers;
groupMembers = [
{AccUserId: user.id, GroupId: groups[0].id, RankId: ranks[0].id},
{AccUserId: user.id, GroupId: groups[1].id, RankId: ranks[2].id}
];
if (i < 3) {
groupMembers.push({AccUserId: user.id, GroupId: groups[2].id, RankId: ranks[1].id});
}
return Promise.join(
GroupMember.bulkCreate(groupMembers),
user.setProducts([
products[(i * 5) + 0],
products[(i * 5) + 1],
products[(i * 5) + 3]
]),
Promise.join(
products[(i * 5) + 0].setTags([
tags[0],
tags[2]
]),
products[(i * 5) + 1].setTags([
tags[1]
]),
products[(i * 5) + 0].setCategory(tags[1]),
products[(i * 5) + 2].setTags([
tags[0]
]),
products[(i * 5) + 3].setTags([
tags[0]
])
),
Promise.join(
products[(i * 5) + 0].setCompany(companies[4]),
products[(i * 5) + 1].setCompany(companies[3]),
products[(i * 5) + 2].setCompany(companies[2]),
products[(i * 5) + 3].setCompany(companies[1]),
products[(i * 5) + 4].setCompany(companies[0])
),
Price.bulkCreate([
{ProductId: products[(i * 5) + 0].id, value: 5},
{ProductId: products[(i * 5) + 0].id, value: 10},
{ProductId: products[(i * 5) + 1].id, value: 5},
{ProductId: products[(i * 5) + 1].id, value: 10},
{ProductId: products[(i * 5) + 1].id, value: 15},
{ProductId: products[(i * 5) + 1].id, value: 20},
{ProductId: products[(i * 5) + 2].id, value: 20},
{ProductId: products[(i * 5) + 3].id, value: 20}
])
);
});
}, []);
});
});
});
});
......
......@@ -291,7 +291,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('allows multiple column unique keys to be defined', function(done) {
it('allows multiple column unique keys to be defined', function() {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: 'user_and_email' },
email: { type: Sequelize.STRING, unique: 'user_and_email' },
......@@ -299,7 +299,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
bCol: { type: Sequelize.STRING, unique: 'a_and_b' }
});
User.sync({ force: true }).on('sql', _.after(2, _.once(function(sql) {
return User.sync({ force: true, logging: _.after(2, _.once(function(sql) {
if (dialect === 'mssql') {
expect(sql).to.match(/CONSTRAINT\s*([`"\[]?user_and_email[`"\]]?)?\s*UNIQUE\s*\([`"\[]?username[`"\]]?, [`"\[]?email[`"\]]?\)/);
expect(sql).to.match(/CONSTRAINT\s*([`"\[]?a_and_b[`"\]]?)?\s*UNIQUE\s*\([`"\[]?aCol[`"\]]?, [`"\[]?bCol[`"\]]?\)/);
......@@ -307,8 +307,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
expect(sql).to.match(/UNIQUE\s*([`"]?user_and_email[`"]?)?\s*\([`"]?username[`"]?, [`"]?email[`"]?\)/);
expect(sql).to.match(/UNIQUE\s*([`"]?a_and_b[`"]?)?\s*\([`"]?aCol[`"]?, [`"]?bCol[`"]?\)/);
}
done();
})));
}))});
});
it('allows unique on column with field aliases', function() {
......@@ -1423,32 +1422,31 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('supports table schema/prefix', function(done) {
it('supports table schema/prefix', function() {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
, prefixUser = self.User.schema('prefix');
var run = function() {
prefixUser.sync({ force: true }).success(function() {
prefixUser.bulkCreate(data).success(function() {
prefixUser.destroy({where: {secretValue: '42'}})
.success(function() {
prefixUser.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(1);
expect(users[0].username).to.equal('Bob');
done();
});
});
});
});
};
this.sequelize.queryInterface.createSchema('prefix').success(function() {
run.call(self);
});
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
, prefixUser = self.User.schema('prefix');
var run = function() {
return prefixUser.sync({ force: true }).then(function() {
return prefixUser.bulkCreate(data).then(function() {
return prefixUser.destroy({where: {secretValue: '42'}}).then(function() {
return prefixUser.findAll({order: 'id'}).then(function(users) {
expect(users.length).to.equal(1);
expect(users[0].username).to.equal('Bob');
});
});
});
});
};
return this.sequelize.queryInterface.dropAllSchemas().then(function() {
return self.sequelize.queryInterface.createSchema('prefix').then(function() {
return run.call(self);
});
});
});
});
......@@ -2022,7 +2020,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('should be able to reference a table with a schema set', function(done) {
it('should be able to reference a table with a schema set', function() {
var self = this;
var UserPub = this.sequelize.define('UserPub', {
......@@ -2038,8 +2036,8 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
var run = function() {
UserPub.sync({ force: true }).success(function() {
ItemPub.sync({ force: true }).on('sql', _.after(2, _.once(function(sql) {
return UserPub.sync({ force: true }).then(function() {
return ItemPub.sync({ force: true, logging: _.after(2, _.once(function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/REFERENCES\s+"prefix"\."UserPubs" \("id"\)/);
} else if (dialect === 'mssql') {
......@@ -2047,17 +2045,19 @@ describe(Support.getTestDialectTeaser('Model'), function() {
} else {
expect(sql).to.match(/REFERENCES\s+`prefix\.UserPubs` \(`id`\)/);
}
done();
})));
}))});
});
};
if (dialect === 'postgres') {
this.sequelize.queryInterface.createSchema('prefix').success(function() {
run.call(self);
return this.sequelize.queryInterface.dropAllSchemas().then(function() {
return self.sequelize.queryInterface.createSchema('prefix').then(function() {
return run.call(self);
});
});
} else {
run.call(self);
return run.call(self);
}
});
......@@ -2127,7 +2127,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('uses an existing dao factory and references the author table', function(done) {
it('uses an existing dao factory and references the author table', function() {
var self = this
, Post = this.sequelize.define('post', {
title: Sequelize.STRING,
......@@ -2142,7 +2142,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter.
Post.sync().on('sql', _.once(function(sql) {
return Post.sync({logging: _.once(function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (Support.dialectIsMySQL()) {
......@@ -2154,12 +2154,10 @@ describe(Support.getTestDialectTeaser('Model'), function() {
} else {
throw new Error('Undefined dialect!');
}
done();
}));
})});
});
it('uses a table name as a string and references the author table', function(done) {
it('uses a table name as a string and references the author table', function() {
var self = this
, Post = self.sequelize.define('post', {
title: Sequelize.STRING,
......@@ -2174,7 +2172,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter.
Post.sync().on('sql', _.once(function(sql) {
return Post.sync({logging: _.once(function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (Support.dialectIsMySQL()) {
......@@ -2186,9 +2184,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
} else {
throw new Error('Undefined dialect!');
}
done();
}));
})});
});
it('emits an error event as the referenced table name is invalid', function(done) {
......@@ -2238,7 +2234,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
});
});
it('works with comments', function(done) {
it('works with comments', function() {
// Test for a case where the comment was being moved to the end of the table when there was also a reference on the column, see #1521
var Member = this.sequelize.define('Member', {})
, Profile = this.sequelize.define('Profile', {
......@@ -2252,30 +2248,25 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}
});
this.sequelize.sync({ force: true }).success(function() {
done();
});
return this.sequelize.sync({ force: true });
});
});
describe('blob', function() {
beforeEach(function(done) {
beforeEach(function() {
this.BlobUser = this.sequelize.define('blobUser', {
data: Sequelize.BLOB
});
this.BlobUser.sync({ force: true }).success(function() {
done();
});
return this.BlobUser.sync({ force: true });
});
describe('buffers', function() {
it('should be able to take a buffer as parameter to a BLOB field', function(done) {
this.BlobUser.create({
it('should be able to take a buffer as parameter to a BLOB field', function() {
return this.BlobUser.create({
data: new Buffer('Sequelize')
}).success(function(user) {
}).then(function(user) {
expect(user).to.be.ok;
done();
});
});
......@@ -2511,40 +2502,37 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}
describe('Unique', function() {
it('should set unique when unique is true', function(done) {
it('should set unique when unique is true', function() {
var self = this;
var uniqueTrue = self.sequelize.define('uniqueTrue', {
str: { type: Sequelize.STRING, unique: true }
});
uniqueTrue.sync({force: true}).on('sql', _.after(2, _.once(function(s) {
return uniqueTrue.sync({force: true, logging: _.after(2, _.once(function(s) {
expect(s).to.match(/UNIQUE/);
done();
})));
}))});
});
it('should not set unique when unique is false', function(done) {
it('should not set unique when unique is false', function() {
var self = this;
var uniqueFalse = self.sequelize.define('uniqueFalse', {
str: { type: Sequelize.STRING, unique: false }
});
uniqueFalse.sync({force: true}).on('sql', _.after(2, _.once(function(s) {
return uniqueFalse.sync({force: true, logging: _.after(2, _.once(function(s) {
expect(s).not.to.match(/UNIQUE/);
done();
})));
}))});
});
it('should not set unique when unique is unset', function(done) {
it('should not set unique when unique is unset', function() {
var self = this;
var uniqueUnset = self.sequelize.define('uniqueUnset', {
str: { type: Sequelize.STRING }
});
uniqueUnset.sync({force: true}).on('sql', _.after(2, _.once(function(s) {
return uniqueUnset.sync({force: true, logging: _.after(2, _.once(function(s) {
expect(s).not.to.match(/UNIQUE/);
done();
})));
}))});
});
});
......
......@@ -114,11 +114,13 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
.sequelizeWithInvalidConnection
.authenticate()
.complete(function(err, result) {
console.log(err.message);
expect(
err.message.match(/connect ECONNREFUSED/) ||
err.message.match(/invalid port number/) ||
err.message.match(/RangeError: Port should be > 0 and < 65536/) ||
err.message.match(/RangeError: port should be > 0 and < 65536/) ||
err.message.match(/RangeError: port should be >= 0 and < 65536: 99999/) ||
err.message.match(/ConnectionError: Login failed for user/)
).to.be.ok;
done();
......
......@@ -10,21 +10,33 @@ var chai = require('chai')
if (current.dialect.supports.transactions) {
describe(Support.getTestDialectTeaser('Sequelize#transaction'), function() {
this.timeout(4000);
describe('success', function() {
it('gets triggered once a transaction has been successfully committed', function(done) {
this
describe('then', function() {
it('gets triggered once a transaction has been successfully committed', function() {
var called = false;
return this
.sequelize
.transaction().then(function(t) { t.commit(); })
.success(function() { done(); });
.transaction().then(function(t) {
return t.commit().then(function () {
called = 1;
});
})
.then(function() {
expect(called).to.be.ok;
});
});
it('gets triggered once a transaction has been successfully rolled back', function(done) {
this
it('gets triggered once a transaction has been successfully rolled back', function() {
var called = false;
return this
.sequelize
.transaction().then(function(t) { t.rollback(); })
.success(function() { done(); });
.transaction().then(function(t) {
return t.rollback().then(function () {
called = 1;
});
})
.then(function() {
expect(called).to.be.ok;
});
});
if (Support.getTestDialect() !== 'sqlite') {
......@@ -71,65 +83,42 @@ describe(Support.getTestDialectTeaser('Sequelize#transaction'), function() {
}
});
describe('error', function() {
if (Support.getTestDialect() === 'sqlite') {
// not sure if we can test this in sqlite ...
// how could we enforce an authentication error in sqlite?
} else {
it('gets triggered once an error occurs', function(done) {
// Supply bad config to force an error
var sequelize = Support.getSequelizeInstance('this', 'is', 'fake config');
sequelize
.transaction().then(function() {})
.catch (function(err) {
expect(err).to.not.be.undefined;
done();
});
});
}
});
describe('complex long running example', function() {
it('works with promise syntax', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
it('works with promise syntax', function() {
return Support.prepareTransactionTest(this.sequelize).then(function(sequelize) {
var Test = sequelize.define('Test', {
id: { type: Support.Sequelize.INTEGER, primaryKey: true, autoIncrement: true},
name: { type: Support.Sequelize.STRING }
});
sequelize
.sync({ force: true })
.then(function() {
sequelize.transaction().then(function(transaction) {
expect(transaction).to.be.instanceOf(Transaction);
Test
.create({ name: 'Peter' }, { transaction: transaction })
.then(function() {
setTimeout(function() {
transaction
.commit()
.then(function() { return Test.count(); })
.then(function(count) {
expect(count).to.equal(1);
done();
});
}, 1000);
return sequelize.sync({ force: true }).then(function() {
return sequelize.transaction().then(function(transaction) {
expect(transaction).to.be.instanceOf(Transaction);
return Test
.create({ name: 'Peter' }, { transaction: transaction })
.then(function() {
return Promise.delay(1000).then(function () {
return transaction
.commit()
.then(function() { return Test.count(); })
.then(function(count) {
expect(count).to.equal(1);
});
});
});
});
});
});
});
});
});
describe('concurrency', function() {
describe('having tables with uniqueness constraints', function() {
beforeEach(function(done) {
beforeEach(function() {
var self = this;
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
return Support.prepareTransactionTest(this.sequelize).then(function(sequelize) {
self.sequelize = sequelize;
self.Model = sequelize.define('Model', {
......@@ -138,9 +127,7 @@ describe(Support.getTestDialectTeaser('Sequelize#transaction'), function() {
timestamps: false
});
self.Model
.sync({ force: true })
.success(function() { done(); });
return self.Model.sync({ force: true });
});
});
......
......@@ -20,7 +20,17 @@ before(function() {
});
beforeEach(function() {
this.sequelize.test.trackRunningQueries();
return Support.clearDatabase(this.sequelize);
});
afterEach(function () {
try {
this.sequelize.test.verifyNoRunningQueries();
} catch (err) {
err.message += " in "+this.currentTest.fullTitle();
throw err;
}
});
module.exports = Support;
......@@ -121,8 +121,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
if (current.dialect.supports.lock) {
describe('row locking', function () {
this.timeout(10000);
it('supports for update', function(done) {
it('supports for update', function() {
var User = this.sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
......@@ -131,10 +130,10 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
, t1Spy = sinon.spy()
, t2Spy = sinon.spy();
this.sequelize.sync({ force: true }).then(function() {
return this.sequelize.sync({ force: true }).then(function() {
return User.create({ username: 'jan'});
}).then(function() {
self.sequelize.transaction().then(function(t1) {
return self.sequelize.transaction().then(function(t1) {
return User.find({
where: {
username: 'jan'
......@@ -143,37 +142,40 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
lock: t1.LOCK.UPDATE,
transaction: t1
}).then(function(t1Jan) {
self.sequelize.transaction({
return self.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
}).then(function(t2) {
User.find({
where: {
username: 'jan'
}
}, {
lock: t2.LOCK.UPDATE,
transaction: t2
}).then(function() {
t2Spy();
t2.commit().then(function() {
expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has comitted
done();
});
});
t1Jan.updateAttributes({
awesome: true
}, { transaction: t1}).then(function() {
t1Spy();
setTimeout(t1.commit.bind(t1), 2000);
});
return Promise.join(
User.find({
where: {
username: 'jan'
}
}, {
lock: t2.LOCK.UPDATE,
transaction: t2
}).then(function() {
t2Spy();
return t2.commit().then(function() {
expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has comitted
});
}),
t1Jan.updateAttributes({
awesome: true
}, { transaction: t1}).then(function() {
t1Spy();
return Promise.delay(2000).then(function () {
return t1.commit();
});
})
);
});
});
});
});
});
it('supports for share', function(done) {
it('supports for share', function() {
var User = this.sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
......@@ -183,10 +185,10 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
, t2FindSpy = sinon.spy()
, t2UpdateSpy = sinon.spy();
this.sequelize.sync({ force: true }).then(function() {
return this.sequelize.sync({ force: true }).then(function() {
return User.create({ username: 'jan'});
}).then(function() {
self.sequelize.transaction().then(function(t1) {
return self.sequelize.transaction().then(function(t1) {
return User.find({
where: {
username: 'jan'
......@@ -195,40 +197,41 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
lock: t1.LOCK.SHARE,
transaction: t1
}).then(function(t1Jan) {
self.sequelize.transaction({
return self.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
}).then(function(t2) {
User.find({
where: {
username: 'jan'
}
}, { transaction: t2}).then(function(t2Jan) {
t2FindSpy();
t2Jan.updateAttributes({
awesome: false
return Promise.join(
User.find({
where: {
username: 'jan'
}
}, { transaction: t2}).then(function(t2Jan) {
t2FindSpy();
return t2Jan.updateAttributes({
awesome: false
}, {
transaction: t2
}).then(function() {
t2UpdateSpy();
return t2.commit().then(function() {
expect(t2FindSpy).to.have.been.calledBefore(t1Spy); // The find call should have returned
expect(t2UpdateSpy).to.have.been.calledAfter(t1Spy); // But the update call should not happen before the first transaction has committed
});
});
}),
t1Jan.updateAttributes({
awesome: true
}, {
transaction: t2
transaction: t1
}).then(function() {
t2UpdateSpy();
t2.commit().then(function() {
expect(t2FindSpy).to.have.been.calledBefore(t1Spy); // The find call should have returned
expect(t2UpdateSpy).to.have.been.calledAfter(t1Spy); // But the update call should not happen before the first transaction has committed
done();
return Promise.delay(2000).then(function () {
t1Spy();
return t1.commit();
});
});
});
t1Jan.updateAttributes({
awesome: true
}, {
transaction: t1
}).then(function() {
setTimeout(function() {
t1Spy();
t1.commit();
}, 2000);
});
})
);
});
});
});
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!