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

Commit be7dfd48 by Jan Aagaard Meier

Removed synconassocation, added ondelete and onupdate to n:m and made cascade th…

…e default, made cascade / set null the default for hasone and belongsto
1 parent c9f39ebe
...@@ -2,6 +2,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -2,6 +2,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
# v2.0.0-dev11 # v2.0.0-dev11
- [PERFORMANCE] increased build performance when using include, which speeds up findAll etc. - [PERFORMANCE] increased build performance when using include, which speeds up findAll etc.
- [FEATURE] n:m now marks the columns of the through table as foreign keys and cascades them on delete and update by default.
- [FEATURE] 1:1 and 1:m marks columns as foreign keys, and sets them to cascade on update and set null on delete. If you are working with an existing DB which does not allow null values, be sure to override those options, or disable them completely by passing useConstrations: false to your assocation call (`M1.belongsTo(M2, { useConstraints: false})`).
#### Backwards compatability changes #### Backwards compatability changes
- selectedValues has been removed for performance reasons, if you depend on this, please open an issue and we will help you work around it. - selectedValues has been removed for performance reasons, if you depend on this, please open an issue and we will help you work around it.
...@@ -9,6 +11,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -9,6 +11,8 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- if you have any 1:1 relations where both sides use an alias, you'll need to set the foreign key, or they'll each use a different foreign key based on their alias. - if you have any 1:1 relations where both sides use an alias, you'll need to set the foreign key, or they'll each use a different foreign key based on their alias.
- foreign keys for non-id primary keys will now be named for the foreign key, i.e. pub_name rather than pub_id - foreign keys for non-id primary keys will now be named for the foreign key, i.e. pub_name rather than pub_id
- if you have non-id primary keys you should go through your associations and set the foreignKey option if relying on a incorrect _id foreign key - if you have non-id primary keys you should go through your associations and set the foreignKey option if relying on a incorrect _id foreign key
- syncOnAssocation has been removed. It only worked for n:m, and having a synchronous function (hasMany) that invokes an asynchronous function (sync) without returning an emitter does not make a lot of sense. If you (implicitly) depended on this feature, sequelize.sync is your friend. If you do not want to do a full sync, use custom through models for n:m (`M1.hasMany(M2, { through: M3})`) and sync the through model explicitly.
- Join tables will be no longer be paranoid (have a deletedAt timestamp added), even though other models are.
# v1.7.0 # v1.7.0
- [FEATURE] covers more advanced include cases with limiting and filtering (specifically cases where a include would be in the subquery but its child include wouldnt be, for cases where a 1:1 association had a 1:M association as a nested include) - [FEATURE] covers more advanced include cases with limiting and filtering (specifically cases where a include would be in the subquery but its child include wouldnt be, for cases where a 1:1 association had a 1:M association as a nested include)
......
...@@ -44,6 +44,10 @@ module.exports = (function() { ...@@ -44,6 +44,10 @@ module.exports = (function() {
var newAttributes = {} var newAttributes = {}
newAttributes[this.identifier] = { type: this.options.keyType || this.target.rawAttributes[this.targetIdentifier].type } newAttributes[this.identifier] = { type: this.options.keyType || this.target.rawAttributes[this.targetIdentifier].type }
if (this.options.useConstraints !== false) {
this.options.onDelete = this.options.onDelete || 'SET NULL'
this.options.onUpdate = this.options.onUpdate || 'CASCADE'
}
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options) Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options)
Utils._.defaults(this.source.rawAttributes, newAttributes) Utils._.defaults(this.source.rawAttributes, newAttributes)
......
...@@ -103,7 +103,8 @@ module.exports = (function() { ...@@ -103,7 +103,8 @@ module.exports = (function() {
if (typeof this.through === "string") { if (typeof this.through === "string") {
this.through = this.sequelize.define(this.through, {}, _.extend(this.options, { this.through = this.sequelize.define(this.through, {}, _.extend(this.options, {
tableName: this.through tableName: this.through,
paranoid: false // A paranoid join table does not make sense
})) }))
if (this.targetAssociation) { if (this.targetAssociation) {
...@@ -174,28 +175,46 @@ module.exports = (function() { ...@@ -174,28 +175,46 @@ module.exports = (function() {
// define a new model, which connects the models // define a new model, which connects the models
var combinedTableAttributes = {} var combinedTableAttributes = {}
var sourceKeyType = this.source.rawAttributes[this.source.primaryKeyAttribute].type , sourceKeyType = this.source.rawAttributes[this.source.primaryKeyAttribute].type
var targetKeyType = this.target.rawAttributes[this.target.primaryKeyAttribute].type , targetKeyType = this.target.rawAttributes[this.target.primaryKeyAttribute].type
, sourceAttribute = { type: sourceKeyType }
, targetAttribute = { type: targetKeyType }
if (this.options.useConstraints !== false) {
sourceAttribute.references = this.source.getTableName()
sourceAttribute.referencesKey = this.source.primaryKeyAttribute
sourceAttribute.onDelete = this.options.onDelete || 'CASCADE'
sourceAttribute.onUpdate = this.options.onUpdate || 'CASCADE'
}
if (this.targetAssociation.options.useConstraints !== false) {
targetAttribute.references = this.target.getTableName()
targetAttribute.referencesKey = this.target.primaryKeyAttribute
targetAttribute.onDelete = this.targetAssociation.options.onDelete || 'CASCADE'
targetAttribute.onUpdate = this.targetAssociation.options.onUpdate || 'CASCADE'
}
if (primaryKeyDeleted) { if (primaryKeyDeleted) {
combinedTableAttributes[this.identifier] = {type: sourceKeyType, primaryKey: true} targetAttribute.primaryKey = sourceAttribute.primaryKey = true
combinedTableAttributes[this.foreignIdentifier] = {type: targetKeyType, primaryKey: true}
} else { } else {
var uniqueKey = [this.through.tableName, this.identifier, this.foreignIdentifier, 'unique'].join('_') var uniqueKey = [this.through.tableName, this.identifier, this.foreignIdentifier, 'unique'].join('_')
combinedTableAttributes[this.identifier] = {type: sourceKeyType, unique: uniqueKey} targetAttribute.unique = sourceAttribute.unique = uniqueKey
combinedTableAttributes[this.foreignIdentifier] = {type: targetKeyType, unique: uniqueKey}
} }
combinedTableAttributes[this.identifier] = sourceAttribute
combinedTableAttributes[this.foreignIdentifier] = targetAttribute
this.through.rawAttributes = Utils._.merge(this.through.rawAttributes, combinedTableAttributes) this.through.rawAttributes = Utils._.merge(this.through.rawAttributes, combinedTableAttributes)
this.through.init(this.through.daoFactoryManager) this.through.init(this.through.daoFactoryManager)
if (this.options.syncOnAssociation) {
this.through.sync()
}
} else { } else {
var newAttributes = {} var newAttributes = {}
var constraintOptions = _.clone(this.options) // Create a new options object for use with addForeignKeyConstraints, to avoid polluting this.options in case it is later used for a n:m
newAttributes[this.identifier] = { type: this.options.keyType || this.target.rawAttributes[this.target.primaryKeyAttribute].type } newAttributes[this.identifier] = { type: this.options.keyType || this.target.rawAttributes[this.target.primaryKeyAttribute].type }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options)
if (this.options.useConstraints !== false) {
constraintOptions.onDelete = constraintOptions.onDelete || 'SET NULL'
constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE'
}
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, constraintOptions)
Utils._.defaults(this.target.rawAttributes, newAttributes) Utils._.defaults(this.target.rawAttributes, newAttributes)
} }
......
...@@ -45,6 +45,11 @@ module.exports = (function() { ...@@ -45,6 +45,11 @@ module.exports = (function() {
newAttributes[this.identifier] = { type: this.options.keyType || keyType } newAttributes[this.identifier] = { type: this.options.keyType || keyType }
Utils._.defaults(this.target.rawAttributes, newAttributes) Utils._.defaults(this.target.rawAttributes, newAttributes)
if (this.options.useConstraints !== false) {
this.options.onDelete = this.options.onDelete || 'SET NULL'
this.options.onUpdate = this.options.onUpdate || 'CASCADE'
}
Helpers.addForeignKeyConstraints(this.target.rawAttributes[this.identifier], this.source, this.target, this.options) Helpers.addForeignKeyConstraints(this.target.rawAttributes[this.identifier], this.source, this.target, this.options)
// Sync attributes and setters/getters to DAO prototype // Sync attributes and setters/getters to DAO prototype
......
var Toposort = require('toposort-class') var Toposort = require('toposort-class')
, DaoFactory = require('./dao-factory') , DaoFactory = require('./dao-factory')
, _ = require('lodash')
module.exports = (function() { module.exports = (function() {
var DAOFactoryManager = function(sequelize) { var DAOFactoryManager = function(sequelize) {
...@@ -39,9 +40,14 @@ module.exports = (function() { ...@@ -39,9 +40,14 @@ module.exports = (function() {
* take foreign key constraints into account so that dependencies are visited * take foreign key constraints into account so that dependencies are visited
* before dependents. * before dependents.
*/ */
DAOFactoryManager.prototype.forEachDAO = function(iterator) { DAOFactoryManager.prototype.forEachDAO = function(iterator, options) {
var daos = {} var daos = {}
, sorter = new Toposort() , sorter = new Toposort()
, sorted
options = _.defaults(options || {}, {
reverse: true
})
this.daos.forEach(function(dao) { this.daos.forEach(function(dao) {
var deps = [] var deps = []
...@@ -62,7 +68,11 @@ module.exports = (function() { ...@@ -62,7 +68,11 @@ module.exports = (function() {
sorter.add(dao.tableName, deps) sorter.add(dao.tableName, deps)
}) })
sorter.sort().reverse().forEach(function(name) { sorted = sorter.sort()
if (options.reverse) {
sorted = sorted.reverse()
}
sorted.forEach(function(name) {
iterator(daos[name], name) iterator(daos[name], name)
}) })
} }
......
...@@ -19,7 +19,6 @@ module.exports = (function() { ...@@ -19,7 +19,6 @@ module.exports = (function() {
validate: {}, validate: {},
freezeTableName: false, freezeTableName: false,
underscored: false, underscored: false,
syncOnAssociation: true,
paranoid: false, paranoid: false,
whereCollection: null, whereCollection: null,
schema: null, schema: null,
...@@ -1609,7 +1608,15 @@ module.exports = (function() { ...@@ -1609,7 +1608,15 @@ module.exports = (function() {
var optClone = function (options) { var optClone = function (options) {
return Utils._.cloneDeep(options, function (elem) { return Utils._.cloneDeep(options, function (elem) {
// The DAOFactories used for include are pass by ref, so don't clone them. // The DAOFactories used for include are pass by ref, so don't clone them.
if (elem instanceof DAOFactory || elem instanceof Utils.col || elem instanceof Utils.literal || elem instanceof Utils.cast || elem instanceof Utils.fn || elem instanceof Utils.and || elem instanceof Utils.or) { if (elem instanceof DAOFactory ||
elem instanceof Utils.col ||
elem instanceof Utils.literal ||
elem instanceof Utils.cast ||
elem instanceof Utils.fn ||
elem instanceof Utils.and ||
elem instanceof Utils.or ||
elem instanceof Transaction
) {
return elem return elem
} }
// Unfortunately, lodash.cloneDeep doesn't preserve Buffer.isBuffer, which we have to rely on for binary data // Unfortunately, lodash.cloneDeep doesn't preserve Buffer.isBuffer, which we have to rely on for binary data
......
...@@ -54,7 +54,15 @@ module.exports = (function() { ...@@ -54,7 +54,15 @@ module.exports = (function() {
if (Utils._.includes(dataType, 'PRIMARY KEY')) { if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys.push(attr) primaryKeys.push(attr)
attrStr.push(this.quoteIdentifier(attr) + " " + dataType.replace(/PRIMARY KEY/, ''))
if (Utils._.includes(dataType, 'REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end
var m = dataType.match(/^(.+) (REFERENCES.*)$/)
attrStr.push(this.quoteIdentifier(attr) + " " + m[1].replace(/PRIMARY KEY/, ''))
foreignKeys[attr] = m[2]
} else {
attrStr.push(this.quoteIdentifier(attr) + " " + dataType.replace(/PRIMARY KEY/, ''))
}
} else if (Utils._.includes(dataType, 'REFERENCES')) { } else if (Utils._.includes(dataType, 'REFERENCES')) {
// MySQL doesn't support inline REFERENCES declarations: move to the end // MySQL doesn't support inline REFERENCES declarations: move to the end
var m = dataType.match(/^(.+) (REFERENCES.*)$/) var m = dataType.match(/^(.+) (REFERENCES.*)$/)
......
...@@ -80,7 +80,7 @@ module.exports = (function() { ...@@ -80,7 +80,7 @@ module.exports = (function() {
} }
var emitter = serial.klass[serial.method].apply(serial.klass, serial.params) var emitter = serial.klass[serial.method].apply(serial.klass, serial.params)
emitter.success(function(result) { emitter.success(function(result) {
self.serialResults[serialCopy.indexOf(serial)] = result self.serialResults[serialCopy.indexOf(serial)] = result
......
...@@ -387,7 +387,7 @@ module.exports = (function() { ...@@ -387,7 +387,7 @@ module.exports = (function() {
// Topologically sort by foreign key constraints to give us an appropriate // Topologically sort by foreign key constraints to give us an appropriate
// creation order // creation order
this.daoFactoryManager.forEachDAO(function(dao, daoName) { this.daoFactoryManager.forEachDAO(function(dao) {
if (dao) { if (dao) {
chainer.add(dao, 'sync', [options]) chainer.add(dao, 'sync', [options])
} else { } else {
...@@ -402,12 +402,12 @@ module.exports = (function() { ...@@ -402,12 +402,12 @@ module.exports = (function() {
var self = this var self = this
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer var chainer = new Utils.QueryChainer()
self.daoFactoryManager.daos.forEach(function(dao) { chainer.add(dao.drop()) }) self.daoFactoryManager.forEachDAO(function(dao) { chainer.add(dao, 'drop', []) }, { reverse: false})
chainer chainer
.run() .runSerially()
.success(function() { emitter.emit('success', null) }) .success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('error', err) }) .error(function(err) { emitter.emit('error', err) })
}).run() }).run()
......
...@@ -239,19 +239,41 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -239,19 +239,41 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING }) , User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User) Task.belongsTo(User) // defaults to SET NULL
this.sequelize.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) { User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() { task.setUser(user).success(function() {
user.destroy().success(function() { user.destroy().success(function() {
Task.findAll().success(function(tasks) { task.reload().success(function() {
expect(tasks).to.have.length(1) expect(task.UserId).to.equal(null)
done()
})
})
})
})
})
})
})
it("should be possible to disable them", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, { useConstraints: false })
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().success(function() {
task.reload().success(function() {
expect(task.UserId).to.equal(user.id)
done() done()
}) })
}) })
...@@ -403,7 +425,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -403,7 +425,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
var tableName = 'TaskXYZ_' + dataType.toString() var tableName = 'TaskXYZ_' + dataType.toString()
Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING }) Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING })
Tasks[dataType].belongsTo(User, { foreignKey: 'userId', keyType: dataType }) Tasks[dataType].belongsTo(User, { foreignKey: 'userId', keyType: dataType, useConstraints: false })
}) })
self.sequelize.sync({ force: true }) self.sequelize.sync({ force: true })
......
...@@ -27,17 +27,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -27,17 +27,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
describe('(1:N)', function() { describe('(1:N)', function() {
describe('hasSingle', function() { describe('hasSingle', function() {
beforeEach(function(done) { beforeEach(function(done) {
var self = this
this.Article = this.sequelize.define('Article', { 'title': DataTypes.STRING }) this.Article = this.sequelize.define('Article', { 'title': DataTypes.STRING })
this.Label = this.sequelize.define('Label', { 'text': DataTypes.STRING }) this.Label = this.sequelize.define('Label', { 'text': DataTypes.STRING })
this.Article.hasMany(this.Label) this.Article.hasMany(this.Label)
this.Label.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Article.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
...@@ -48,18 +44,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -48,18 +44,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Article.hasMany(Label) Article.hasMany(Label)
sequelize.sync({ force: true }).success(function() { sequelize.drop().success(function () {
Article.create({ title: 'foo' }).success(function(article) { sequelize.sync({ force: true }).success(function() {
Label.create({ text: 'bar' }).success(function(label) { Article.create({ title: 'foo' }).success(function(article) {
sequelize.transaction(function(t) { Label.create({ text: 'bar' }).success(function(label) {
article.setLabels([ label ], { transaction: t }).success(function() { sequelize.transaction(function(t) {
Article.all({ transaction: t }).success(function(articles) { article.setLabels([ label ], { transaction: t }).success(function() {
articles[0].hasLabel(label).success(function(hasLabel) { Article.all({ transaction: t }).success(function(articles) {
expect(hasLabel).to.be.false articles[0].hasLabel(label).success(function(hasLabel) {
Article.all({ transaction: t }).success(function(articles) { expect(hasLabel).to.be.false
articles[0].hasLabel(label, { transaction: t }).success(function(hasLabel) { Article.all({ transaction: t }).success(function(articles) {
expect(hasLabel).to.be.true articles[0].hasLabel(label, { transaction: t }).success(function(hasLabel) {
t.rollback().success(function() { done() }) expect(hasLabel).to.be.true
t.rollback().success(function() { done() })
})
}) })
}) })
}) })
...@@ -74,7 +72,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -74,7 +72,6 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
it('does not have any labels assigned to it initially', function(done) { it('does not have any labels assigned to it initially', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Articl2e' }),
this.Article.create({ title: 'Article' }), this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }), this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' }) this.Label.create({ text: 'Epicness' })
...@@ -130,10 +127,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -130,10 +127,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.Article.hasMany(this.Label) this.Article.hasMany(this.Label)
this.Label.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Article.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
...@@ -144,18 +139,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -144,18 +139,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Article.hasMany(Label) Article.hasMany(Label)
sequelize.sync({ force: true }).success(function() { sequelize.drop().success(function () {
Article.create({ title: 'foo' }).success(function(article) { sequelize.sync({ force: true }).success(function() {
Label.create({ text: 'bar' }).success(function(label) { Article.create({ title: 'foo' }).success(function(article) {
sequelize.transaction(function(t) { Label.create({ text: 'bar' }).success(function(label) {
article.setLabels([ label ], { transaction: t }).success(function() { sequelize.transaction(function(t) {
Article.all({ transaction: t }).success(function(articles) { article.setLabels([ label ], { transaction: t }).success(function() {
articles[0].hasLabels([ label ]).success(function(hasLabel) { Article.all({ transaction: t }).success(function(articles) {
expect(hasLabel).to.be.false articles[0].hasLabels([ label ]).success(function(hasLabel) {
Article.all({ transaction: t }).success(function(articles) { expect(hasLabel).to.be.false
articles[0].hasLabels([ label ], { transaction: t }).success(function(hasLabel) { Article.all({ transaction: t }).success(function(articles) {
expect(hasLabel).to.be.true articles[0].hasLabels([ label ], { transaction: t }).success(function(hasLabel) {
t.rollback().success(function() { done() }) expect(hasLabel).to.be.true
t.rollback().success(function() { done() })
})
}) })
}) })
}) })
...@@ -242,19 +239,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -242,19 +239,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Task.hasMany(User) Task.hasMany(User)
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { User.create({ username: 'foo' }).success(function(user) {
User.create({ username: 'foo' }).success(function(user) { Task.create({ title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) { task.setUsers([ user ]).success(function() {
task.setUsers([ user ]).success(function() { task.getUsers().success(function(_users) {
task.getUsers().success(function(_users) { expect(_users).to.have.length(1)
expect(_users).to.have.length(1)
task.setUsers(null).success(function() { task.setUsers(null).success(function() {
task.getUsers().success(function(_users) { task.getUsers().success(function(_users) {
expect(_users).to.have.length(0) expect(_users).to.have.length(0)
done() done()
})
}) })
}) })
}) })
...@@ -307,19 +302,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -307,19 +302,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Task.hasMany(User) Task.hasMany(User)
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { User.create({ username: 'foo' }).success(function(user) {
User.create({ username: 'foo' }).success(function(user) { Task.create({ title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) { task.setUsers([ user ]).success(function() {
task.setUsers([ user ]).success(function() { task.getUsers().success(function(_users) {
task.getUsers().success(function(_users) { expect(_users).to.have.length(1)
expect(_users).to.have.length(1)
task.setUsers(null).success(function() { task.setUsers(null).success(function() {
task.getUsers().success(function(_users) { task.getUsers().success(function(_users) {
expect(_users).to.have.length(0) expect(_users).to.have.length(0)
done() done()
})
}) })
}) })
}) })
...@@ -391,18 +384,16 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -391,18 +384,16 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.User.hasMany(self.Task) this.User.hasMany(self.Task)
this.User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() { var chainer = new Sequelize.Utils.QueryChainer([
var chainer = new Sequelize.Utils.QueryChainer([ self.User.create({ username: 'John'}),
self.User.create({ username: 'John'}), self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Get rich', active: true}), self.Task.create({ title: 'Die trying', active: false})
self.Task.create({ title: 'Die trying', active: false}) ])
])
chainer.run().success(function (results, john, task1, task2) { chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(function() { john.setTasks([task1, task2]).success(function() {
done() done()
})
}) })
}) })
}) })
...@@ -420,8 +411,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -420,8 +411,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.Article.hasMany(this.Label) this.Article.hasMany(this.Label)
this.Label.sync({ force: true }).success(function() { this.sequelize.drop().success(function () {
self.Article.sync({ force: true }).success(function() { self.sequelize.sync({ force: true }).success(function() {
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
self.Article.create({ title: 'Article' }), self.Article.create({ title: 'Article' }),
self.Label.create({ text: 'Awesomeness', until: '2014-01-01 01:00:00' }), self.Label.create({ text: 'Awesomeness', until: '2014-01-01 01:00:00' }),
...@@ -549,21 +540,19 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -549,21 +540,19 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.User = this.sequelize.define('User', { username: DataTypes.STRING }) this.User = this.sequelize.define('User', { username: DataTypes.STRING })
this.Task = this.sequelize.define('Task', { title: DataTypes.STRING, active: DataTypes.BOOLEAN }) this.Task = this.sequelize.define('Task', { title: DataTypes.STRING, active: DataTypes.BOOLEAN })
self.User.hasMany(self.Task) this.User.hasMany(this.Task)
self.Task.hasMany(self.User) this.Task.hasMany(this.User)
this.User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() { var chainer = new Sequelize.Utils.QueryChainer([
var chainer = new Sequelize.Utils.QueryChainer([ self.User.create({ username: 'John'}),
self.User.create({ username: 'John'}), self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Get rich', active: true}), self.Task.create({ title: 'Die trying', active: false})
self.Task.create({ title: 'Die trying', active: false}) ])
])
chainer.run().success(function (results, john, task1, task2) { chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(function() { john.setTasks([task1, task2]).success(function() {
done() done()
})
}) })
}) })
}) })
...@@ -577,18 +566,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -577,18 +566,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Article.hasMany(Label) Article.hasMany(Label)
Label.hasMany(Article) Label.hasMany(Article)
sequelize.sync({ force: true }).success(function() { sequelize.drop().success(function () {
Article.create({ title: 'foo' }).success(function(article) { sequelize.sync({ force: true }).success(function() {
Label.create({ text: 'bar' }).success(function(label) { Article.create({ title: 'foo' }).success(function(article) {
sequelize.transaction(function(t) { Label.create({ text: 'bar' }).success(function(label) {
article.setLabels([ label ], { transaction: t }).success(function() { sequelize.transaction(function(t) {
Article.all({ transaction: t }).success(function(articles) { article.setLabels([ label ], { transaction: t }).success(function() {
articles[0].getLabels().success(function(labels) { Article.all({ transaction: t }).success(function(articles) {
expect(labels).to.have.length(0) articles[0].getLabels().success(function(labels) {
Article.all({ transaction: t }).success(function(articles) { expect(labels).to.have.length(0)
articles[0].getLabels({ transaction: t }).success(function(labels) { Article.all({ transaction: t }).success(function(articles) {
expect(labels).to.have.length(1) articles[0].getLabels({ transaction: t }).success(function(labels) {
t.rollback().success(function() { done() }) expect(labels).to.have.length(1)
t.rollback().success(function() { done() })
})
}) })
}) })
}) })
...@@ -658,19 +649,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -658,19 +649,17 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
User.hasMany(Task) User.hasMany(Task)
Task.hasMany(User) Task.hasMany(User)
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { User.create({ username: 'foo' }).success(function(user) {
User.create({ username: 'foo' }).success(function(user) { Task.create({ title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) { task.setUsers([ user ]).success(function() {
task.setUsers([ user ]).success(function() { task.getUsers().success(function(_users) {
task.getUsers().success(function(_users) { expect(_users).to.have.length(1)
expect(_users).to.have.length(1)
task.setUsers(null).success(function() { task.setUsers(null).success(function() {
task.getUsers().success(function(_users) { task.getUsers().success(function(_users) {
expect(_users).to.have.length(0) expect(_users).to.have.length(0)
done() done()
})
}) })
}) })
}) })
...@@ -693,20 +682,18 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -693,20 +682,18 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Group.hasMany(Member, {joinTableName: 'group_members', foreignKey: 'group_id'}) Group.hasMany(Member, {joinTableName: 'group_members', foreignKey: 'group_id'})
Member.hasMany(Group, {joinTableName: 'group_members', foreignKey: 'member_id'}) Member.hasMany(Group, {joinTableName: 'group_members', foreignKey: 'member_id'})
Group.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Member.sync({ force: true }).success(function() { Group.create({group_id: 1, name: 'Group1'}).success(function(){
Group.create({group_id: 1, name: 'Group1'}).success(function(){ Member.create({member_id: 10, email: 'team@sequelizejs.com'}).success(function() {
Member.create({member_id: 10, email: 'team@sequelizejs.com'}).success(function() { Group.find(1).success(function(group) {
Group.find(1).success(function(group) { Member.find(10).success(function(member) {
Member.find(10).success(function(member) { group.addMember(member).success(function() {
group.addMember(member).success(function() { group.getMembers().success(function(members) {
group.getMembers().success(function(members) { expect(members).to.be.instanceof(Array)
expect(members).to.be.instanceof(Array) expect(members).to.have.length(1)
expect(members).to.have.length(1) expect(members[0].member_id).to.equal(10)
expect(members[0].member_id).to.equal(10) expect(members[0].email).to.equal('team@sequelizejs.com')
expect(members[0].email).to.equal('team@sequelizejs.com') done()
done()
})
}) })
}) })
}) })
...@@ -725,15 +712,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -725,15 +712,13 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
User.hasMany(Task) User.hasMany(Task)
Task.hasMany(User) Task.hasMany(User)
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { Task.create({ title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) { task.createUser({ username: 'foo' }).success(function() {
task.createUser({ username: 'foo' }).success(function() { task.getUsers().success(function(_users) {
task.getUsers().success(function(_users) { expect(_users).to.have.length(1)
expect(_users).to.have.length(1)
done() done()
})
}) })
}) })
}) })
...@@ -749,18 +734,16 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -749,18 +734,16 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
User.hasMany(Task) User.hasMany(Task)
Task.hasMany(User) Task.hasMany(User)
User.sync({ force: true }).success(function() { sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { Task.create({ title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) { sequelize.transaction(function (t) {
sequelize.transaction(function (t) { task.createUser({ username: 'foo' }, { transaction: t }).success(function() {
task.createUser({ username: 'foo' }, { transaction: t }).success(function() { task.getUsers().success(function(users) {
task.getUsers().success(function(users) { expect(users).to.have.length(0)
expect(users).to.have.length(0)
task.getUsers({ transaction: t }).success(function(users) {
task.getUsers({ transaction: t }).success(function(users) { expect(users).to.have.length(1)
expect(users).to.have.length(1) t.rollback().success(function() { done() })
t.rollback().success(function() { done() })
})
}) })
}) })
}) })
...@@ -810,10 +793,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -810,10 +793,8 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
this.User.hasMany(this.Task) this.User.hasMany(this.Task)
this.Task.hasMany(this.User) this.Task.hasMany(this.User)
this.User.sync({ force: true }).success(function() { this.sequelize.sync({force: true}).success(function() {
self.Task.sync({force: true}).success(function() { done()
done()
})
}) })
}) })
...@@ -880,7 +861,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -880,7 +861,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
it('uses the specified joinTableName or a reasonable default', function(done) { it('uses the specified joinTableName or a reasonable default', function() {
for (var associationName in this.User.associations) { for (var associationName in this.User.associations) {
expect(associationName).not.to.equal(this.User.tableName) expect(associationName).not.to.equal(this.User.tableName)
expect(associationName).not.to.equal(this.Task.tableName) expect(associationName).not.to.equal(this.Task.tableName)
...@@ -894,9 +875,26 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -894,9 +875,26 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
expect(tableName).to.equal(associationName) expect(tableName).to.equal(associationName)
} }
} }
setTimeout(function () { })
done()
}, 50) it('makes join table non-paranoid by default', function () {
var paranoidSequelize = new Sequelize('','','', {
define: {
paranoid: true
}
})
, ParanoidUser = paranoidSequelize.define('ParanoidUser', {})
, ParanoidTask = paranoidSequelize.define('ParanoidTask', {})
ParanoidUser.hasMany(ParanoidTask)
ParanoidTask.hasMany(ParanoidUser)
expect(ParanoidUser.options.paranoid).to.be.ok
expect(ParanoidTask.options.paranoid).to.be.ok
_.forEach(ParanoidUser.associations, function (association) {
expect(association.through.options.paranoid).not.to.be.ok
})
}) })
}) })
...@@ -1360,20 +1358,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1360,20 +1358,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { describe('1:m', function () {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) it("sets null by default", function(done) {
, User = this.sequelize.define('User', { username: DataTypes.STRING }) var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task) User.hasMany(Task)
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) { User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() { user.setTasks([task]).success(function() {
user.destroy().success(function() { user.destroy().success(function() {
Task.findAll().success(function(tasks) { task.reload().success(function() {
expect(tasks).to.have.length(1) expect(task.UserId).to.equal(null)
done() done()
}) })
}) })
...@@ -1382,22 +1380,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1382,22 +1380,20 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
})
it("can cascade deletes", function(done) { it("should be possible to remove all constraints", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING }) , User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task, {onDelete: 'cascade'}) User.hasMany(Task, { useConstraints: false })
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) { User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() { user.setTasks([task]).success(function() {
user.destroy().success(function() { user.destroy().success(function() {
Task.findAll().success(function(tasks) { task.reload().success(function() {
expect(tasks).to.have.length(0) expect(task.UserId).to.equal(user.id)
done() done()
}) })
}) })
...@@ -1406,24 +1402,156 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1406,24 +1402,156 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task, {onDelete: 'cascade'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(0)
done()
})
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task, {onDelete: 'restrict'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task, {onUpdate: 'cascade'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
expect(tasks[0].UserId).to.equal(999)
done()
})
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
User.hasMany(Task, {onUpdate: 'restrict'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
}) })
it("can restrict deletes", function(done) { describe('n:m', function () {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) beforeEach(function () {
, User = this.sequelize.define('User', { username: DataTypes.STRING }) this.Task = this.sequelize.define('task', { title: DataTypes.STRING })
this.User = this.sequelize.define('user', { username: DataTypes.STRING })
this.UserTasks = this.sequelize.define('tasksusers', { userId: DataTypes.INTEGER, taskId: DataTypes.INTEGER })
})
User.hasMany(Task, {onDelete: 'restrict'}) it("can cascade deletes both ways by default", function (done) {
var _done = _.after(2, done)
, self = this
User.sync({ force: true }).success(function() { self.User.hasMany(self.Task)
Task.sync({ force: true }).success(function() { self.Task.hasMany(self.User)
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) { this.sequelize.sync({ force: true }).success(function() {
self.User.create({ id: 67, username: 'foo' }).success(function(user) {
self.Task.create({ id: 52, title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() { user.setTasks([task]).success(function() {
user.destroy().error(function() { user.destroy().success(function() {
// Should fail due to FK restriction self.UserTasks.findAll({ where: { userId: user.id }}).success(function(usertasks) {
Task.findAll().success(function(tasks) { expect(usertasks).to.have.length(0)
expect(tasks).to.have.length(1) _done()
done() })
})
})
})
})
self.User.create({ id: 89, username: 'bar' }).success(function(user) {
self.Task.create({ id: 42, title: 'kast' }).success(function(task) {
task.setUsers([user]).success(function() {
task.destroy().success(function() {
self.UserTasks.findAll({ where: { taskId: task.id }}).success(function(usertasks) {
expect(usertasks).to.have.length(0)
_done()
}) })
}) })
}) })
...@@ -1431,31 +1559,66 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1431,31 +1559,66 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
})
it("can cascade updates", function(done) { it("can restrict deletes both ways", function (done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) var _done = _.after(2, done)
, User = this.sequelize.define('User', { username: DataTypes.STRING }) , self = this
User.hasMany(Task, {onUpdate: 'cascade'}) self.User.hasMany(self.Task, { onDelete: 'RESTRICT'})
self.Task.hasMany(self.User, { onDelete: 'RESTRICT'})
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { self.User.create({ id: 67, username: 'foo' }).success(function(user) {
User.create({ username: 'foo' }).success(function(user) { self.Task.create({ id: 52, title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() { user.setTasks([task]).success(function() {
user.destroy().error(function() {
// This should error, because updates are restricted
_done()
})
})
})
})
// Changing the id of a DAO requires a little dance since self.User.create({ id: 89, username: 'bar' }).success(function(user) {
// the `UPDATE` query generated by `save()` uses `id` in the self.Task.create({ id: 42, title: 'kast' }).success(function(task) {
// `WHERE` clause task.setUsers([user]).success(function() {
task.destroy().error(function () {
// This should error, because updates are restricted
_done()
})
})
})
})
})
})
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model) it("can cascade and restrict deletes", function (done) {
user.QueryInterface.update(user, tableName, {id: 999}, user.id) var _done = _.after(2, done)
.success(function() { , self = this
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1) self.User.hasMany(self.Task, { onDelete: 'RESTRICT'})
expect(tasks[0].UserId).to.equal(999) self.Task.hasMany(self.User) // Implicit CASCADE
done()
this.sequelize.sync({ force: true }).success(function() {
self.User.create({ id: 67, username: 'foo' }).success(function(user) {
self.Task.create({ id: 52, title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().error(function() {
// This should error, because deletes are restricted
_done()
})
})
})
})
self.User.create({ id: 89, username: 'bar' }).success(function(user) {
self.Task.create({ id: 42, title: 'kast' }).success(function(task) {
task.setUsers([user]).success(function() {
task.destroy().success(function () {
self.UserTasks.findAll({ where: { taskId: task.id }}).success(function(usertasks) {
// This should not exist because deletes cascade
expect(usertasks).to.have.length(0)
_done()
}) })
}) })
}) })
...@@ -1463,31 +1626,37 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1463,31 +1626,37 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
})
it("can restrict updates", function(done) { it("should be possible to remove all constraints", function (done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) var _done = _.after(2, done)
, User = this.sequelize.define('User', { username: DataTypes.STRING }) , self = this
User.hasMany(Task, {onUpdate: 'restrict'}) self.User.hasMany(self.Task, { useConstraints: false })
self.Task.hasMany(self.User, { useConstraints: false })
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { self.User.create({ id: 67, username: 'foo' }).success(function(user) {
User.create({ username: 'foo' }).success(function(user) { self.Task.create({ id: 52, title: 'task' }).success(function(task) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() { user.setTasks([task]).success(function() {
user.destroy().success(function () {
self.UserTasks.findAll({ where: { userId: user.id }}).success(function(usertasks) {
// When we're not using foreign keys, join table rows will be orphaned
expect(usertasks).to.have.length(1)
_done()
})
})
})
})
})
// Changing the id of a DAO requires a little dance since self.User.create({ id: 89, username: 'bar' }).success(function(user) {
// the `UPDATE` query generated by `save()` uses `id` in the self.Task.create({ id: 42, title: 'kast' }).success(function(task) {
// `WHERE` clause task.setUsers([user]).success(function() {
task.destroy().success(function () {
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.Model) self.UserTasks.findAll({ where: { taskId: task.id }}).success(function(usertasks) {
user.QueryInterface.update(user, tableName, {id: 999}, user.id) // When we're not using foreign keys, join table rows will be orphaned
.error(function() { expect(usertasks).to.have.length(1)
// Should fail due to FK restriction _done()
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
}) })
}) })
}) })
...@@ -1509,7 +1678,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1509,7 +1678,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
var tableName = 'TaskXYZ_' + dataType.toString() var tableName = 'TaskXYZ_' + dataType.toString()
Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING }) Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING })
User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType }) User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, useConstraints: false })
Tasks[dataType].sync({ force: true }).success(function() { Tasks[dataType].sync({ force: true }).success(function() {
expect(Tasks[dataType].rawAttributes.userId.type.toString()) expect(Tasks[dataType].rawAttributes.userId.type.toString())
......
...@@ -212,11 +212,11 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -212,11 +212,11 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING }) , User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task) User.hasOne(Task) // defaults to set NULL
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { Task.sync({ force: true }).success(function() {
...@@ -224,8 +224,32 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -224,8 +224,32 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
Task.create({ title: 'task' }).success(function(task) { Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() { user.setTask(task).success(function() {
user.destroy().success(function() { user.destroy().success(function() {
Task.findAll().success(function(tasks) { task.reload().success(function() {
expect(tasks).to.have.length(1) expect(task.UserId).to.equal(null)
done()
})
})
})
})
})
})
})
})
it("should be possible to disable them", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, { useConstraints: false })
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().success(function() {
task.reload().success(function() {
expect(task.UserId).to.equal(user.id)
done() done()
}) })
}) })
...@@ -387,7 +411,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -387,7 +411,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
var tableName = 'TaskXYZ_' + dataType.toString() var tableName = 'TaskXYZ_' + dataType.toString()
Tasks[dataType] = self.sequelize.define(tableName, { title: Sequelize.STRING }) Tasks[dataType] = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasOne(Tasks[dataType], { foreignKey: 'userId', keyType: dataType }) User.hasOne(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, useConstraints: false })
Tasks[dataType].sync({ force: true }).success(function() { Tasks[dataType].sync({ force: true }).success(function() {
expect(Tasks[dataType].rawAttributes.userId.type.toString()) expect(Tasks[dataType].rawAttributes.userId.type.toString())
......
...@@ -277,14 +277,15 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -277,14 +277,15 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING }) self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
this.init = function(callback) { this.init = function(callback) {
self.Task.sync({ force: true }).success(function() { self.sequelize.drop().success(function () {
self.Worker.sync({ force: true }).success(function() { self.sequelize.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) { self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) { self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker self.worker = worker
self.task = task self.task = task
callback() callback()
}) })
}) })
}) })
}) })
...@@ -386,35 +387,32 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -386,35 +387,32 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.User.belongsTo(self.Group) self.User.belongsTo(self.Group)
self.sequelize.sync({ force: true }).success(function() { self.sequelize.sync({ force: true }).success(function() {
self.User.create({ username: 'someone', GroupPKeagerbelongName: 'people' }).success(function() { self.Group.create({ name: 'people' }).success(function() {
self.Group.create({ name: 'people' }).success(function() { self.User.create({ username: 'someone', GroupPKeagerbelongName: 'people' }).success(function() {
self.User.find({ self.User.find({
where: { where: {
username: 'someone' username: 'someone'
}, },
include: [self.Group] include: [self.Group]
}).complete(function (err, someUser) { }).complete(function (err, someUser) {
expect(err).to.be.null expect(err).to.be.null
expect(someUser).to.exist expect(someUser).to.exist
expect(someUser.username).to.equal('someone') expect(someUser.username).to.equal('someone')
expect(someUser.groupPKeagerbelong.name).to.equal('people') expect(someUser.groupPKeagerbelong.name).to.equal('people')
done() done()
}) })
}) })
}) })
}) })
}) })
it('getting parent data in many to one relationship', function(done) { it('getting parent data in many to one relationship', function(done) {
var User = this.sequelize.define('User', {
var self = this;
var User = self.sequelize.define('User', {
id: {type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true}, id: {type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true},
username: {type: Sequelize.STRING} username: {type: Sequelize.STRING}
}) })
var Message = self.sequelize.define('Message', { var Message = this.sequelize.define('Message', {
id: {type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true}, id: {type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true},
user_id: {type: Sequelize.INTEGER}, user_id: {type: Sequelize.INTEGER},
message: {type: Sequelize.STRING} message: {type: Sequelize.STRING}
...@@ -423,41 +421,35 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -423,41 +421,35 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
User.hasMany(Message) User.hasMany(Message)
Message.belongsTo(User, { foreignKey: 'user_id' }) Message.belongsTo(User, { foreignKey: 'user_id' })
Message.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
User.sync({ force: true }).success(function() { User.create({username: 'test_testerson'}).success(function(user) {
User.create({username: 'test_testerson'}).success(function(user) { Message.create({user_id: user.id, message: 'hi there!'}).success(function(message) {
Message.create({user_id: user.id, message: 'hi there!'}).success(function(message) { Message.create({user_id: user.id, message: 'a second message'}).success(function(message) {
Message.create({user_id: user.id, message: 'a second message'}).success(function(message) { Message.findAll({
Message.findAll({
where: {user_id: user.id},
attributes: [
'user_id',
'message'
],
include: [{ model: User, attributes: ['username'] }]
}).success(function(messages) { where: {user_id: user.id},
attributes: [
'user_id',
'message'
],
include: [{ model: User, attributes: ['username'] }]
expect(messages.length).to.equal(2); }).success(function(messages) {
expect(messages.length).to.equal(2);
expect(messages[0].message).to.equal('hi there!'); expect(messages[0].message).to.equal('hi there!');
expect(messages[0].user.username).to.equal('test_testerson'); expect(messages[0].user.username).to.equal('test_testerson');
expect(messages[1].message).to.equal('a second message'); expect(messages[1].message).to.equal('a second message');
expect(messages[1].user.username).to.equal('test_testerson'); expect(messages[1].user.username).to.equal('test_testerson');
done() done()
})
}) })
}) })
}) })
}) })
}) })
}) })
it('allows mulitple assocations of the same model with different alias', function (done) { it('allows mulitple assocations of the same model with different alias', function (done) {
...@@ -527,21 +519,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -527,21 +519,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
self.Group.hasOne(self.User) self.Group.hasOne(self.User)
self.sequelize.sync({ force: true }).success(function() { self.sequelize.drop().success(function () {
self.User.create({ username: 'someone', GroupPKeageroneName: 'people' }).success(function() { self.sequelize.sync({ force: true }).success(function() {
self.Group.create({ name: 'people' }).success(function() { self.Group.create({ name: 'people' }).success(function() {
self.Group.find({ self.User.create({ username: 'someone', GroupPKeageroneName: 'people' }).success(function() {
where: { self.Group.find({
name: 'people' where: {
}, name: 'people'
include: [self.User] },
}).complete(function (err, someGroup) { include: [self.User]
expect(err).to.be.null }).complete(function (err, someGroup) {
expect(someGroup).to.exist expect(err).to.be.null
expect(someGroup.name).to.equal('people') expect(someGroup).to.exist
expect(someGroup.userPKeagerone.username).to.equal('someone') expect(someGroup.name).to.equal('people')
done() expect(someGroup.userPKeagerone.username).to.equal('someone')
}) done()
})
})
}) })
}) })
}) })
...@@ -660,25 +654,27 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -660,25 +654,27 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Contact.hasMany(self.Photo, { as: 'Photos' }) self.Contact.hasMany(self.Photo, { as: 'Photos' })
self.Contact.hasMany(self.PhoneNumber) self.Contact.hasMany(self.PhoneNumber)
self.sequelize.sync({ force: true }).success(function() { self.sequelize.drop().success(function () {
self.Contact.create({ name: 'Boris' }).success(function(someContact) { self.sequelize.sync({ force: true }).success(function() {
self.Photo.create({ img: 'img.jpg' }).success(function(somePhoto) { self.Contact.create({ name: 'Boris' }).success(function(someContact) {
self.PhoneNumber.create({ phone: '000000' }).success(function(somePhone1) { self.Photo.create({ img: 'img.jpg' }).success(function(somePhoto) {
self.PhoneNumber.create({ phone: '111111' }).success(function(somePhone2) { self.PhoneNumber.create({ phone: '000000' }).success(function(somePhone1) {
someContact.setPhotos([somePhoto]).complete(function (err, data) { self.PhoneNumber.create({ phone: '111111' }).success(function(somePhone2) {
expect(err).to.be.null someContact.setPhotos([somePhoto]).complete(function (err, data) {
someContact.setPhoneNumbers([somePhone1, somePhone2]).complete(function (err, data) { expect(err).to.be.null
self.Contact.find({ someContact.setPhoneNumbers([somePhone1, somePhone2]).complete(function (err, data) {
where: { self.Contact.find({
name: 'Boris' where: {
}, name: 'Boris'
include: [self.PhoneNumber, { daoFactory: self.Photo, as: 'Photos' }] },
}).complete(function (err, fetchedContact) { include: [self.PhoneNumber, { daoFactory: self.Photo, as: 'Photos' }]
expect(err).to.be.null }).complete(function (err, fetchedContact) {
expect(fetchedContact).to.exist expect(err).to.be.null
expect(fetchedContact.photos.length).to.equal(1) expect(fetchedContact).to.exist
expect(fetchedContact.phoneNumbers.length).to.equal(2) expect(fetchedContact.photos.length).to.equal(1)
done() expect(fetchedContact.phoneNumbers.length).to.equal(2)
done()
})
}) })
}) })
}) })
...@@ -706,22 +702,24 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -706,22 +702,24 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Group.hasMany(self.User) self.Group.hasMany(self.User)
self.User.hasMany(self.Group) self.User.hasMany(self.Group)
self.sequelize.sync({ force: true }).success(function() { self.sequelize.drop().success(function () {
self.User.create({ username: 'someone' }).success(function(someUser) { self.sequelize.sync({ force: true }).success(function() {
self.Group.create({ name: 'people' }).success(function(someGroup) { self.User.create({ username: 'someone' }).success(function(someUser) {
someUser.setGroupPKeagerones([someGroup]).complete(function (err, data) { self.Group.create({ name: 'people' }).success(function(someGroup) {
expect(err).to.be.null someUser.setGroupPKeagerones([someGroup]).complete(function (err, data) {
self.User.find({
where: {
username: 'someone'
},
include: [self.Group]
}).complete(function (err, someUser) {
expect(err).to.be.null expect(err).to.be.null
expect(someUser).to.exist self.User.find({
expect(someUser.username).to.equal('someone') where: {
expect(someUser.groupPKeagerones[0].name).to.equal('people') username: 'someone'
done() },
include: [self.Group]
}).complete(function (err, someUser) {
expect(err).to.be.null
expect(someUser).to.exist
expect(someUser.username).to.equal('someone')
expect(someUser.groupPKeagerones[0].name).to.equal('people')
done()
})
}) })
}) })
}) })
......
...@@ -313,18 +313,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -313,18 +313,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
User.hasMany(Session, { as: 'Sessions' }) User.hasMany(Session, { as: 'Sessions' })
Session.belongsTo(User) Session.belongsTo(User)
Session.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
User.sync({ force: true }).success(function() { User.create({name: 'Name1', password: '123', isAdmin: false}).success(function(user) {
User.create({name: 'Name1', password: '123', isAdmin: false}).success(function(user) { var sess = Session.build({
var sess = Session.build({ lastUpdate: new Date(),
lastUpdate: new Date(), token: '123'
token: '123' })
})
user.addSession(sess).success(function(u) { user.addSession(sess).success(function(u) {
expect(u.token).to.equal('123') expect(u.token).to.equal('123')
done() done()
})
}) })
}) })
}) })
...@@ -849,9 +847,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -849,9 +847,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Country.hasMany(self.Person, { as: 'Residents', foreignKey: 'CountryResidentId' }) self.Country.hasMany(self.Person, { as: 'Residents', foreignKey: 'CountryResidentId' })
self.Person.belongsTo(self.Country, { as: 'CountryResident', foreignKey: 'CountryResidentId' }) self.Person.belongsTo(self.Country, { as: 'CountryResident', foreignKey: 'CountryResidentId' })
async.forEach([ self.Continent, self.Country, self.Industry, self.Person ], function(model, callback) { this.sequelize.sync({ force: true }).success(function () {
model.sync({ force: true }).done(callback)
}, function () {
async.parallel({ async.parallel({
europe: function(callback) {self.Continent.create({ name: 'Europe' }).done(callback)}, europe: function(callback) {self.Continent.create({ name: 'Europe' }).done(callback)},
england: function(callback) {self.Country.create({ name: 'England' }).done(callback)}, england: function(callback) {self.Country.create({ name: 'England' }).done(callback)},
...@@ -874,7 +870,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -874,7 +870,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done() done()
}) })
}) })
}) })
}) })
it('includes all associations', function(done) { it('includes all associations', function(done) {
...@@ -965,9 +961,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -965,9 +961,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Country.hasMany(self.Person, { as: 'Residents', foreignKey: 'CountryResidentId' }) self.Country.hasMany(self.Person, { as: 'Residents', foreignKey: 'CountryResidentId' })
self.Person.belongsTo(self.Country, { as: 'CountryResident', foreignKey: 'CountryResidentId' }) self.Person.belongsTo(self.Country, { as: 'CountryResident', foreignKey: 'CountryResidentId' })
async.forEach([ self.Continent, self.Country, self.Person ], function(model, callback) { this.sequelize.sync({ force: true }).success(function () {
model.sync({ force: true }).done(callback)
}, function () {
async.parallel({ async.parallel({
europe: function(callback) {self.Continent.create({ name: 'Europe' }).done(callback)}, europe: function(callback) {self.Continent.create({ name: 'Europe' }).done(callback)},
asia: function(callback) {self.Continent.create({ name: 'Asia' }).done(callback)}, asia: function(callback) {self.Continent.create({ name: 'Asia' }).done(callback)},
...@@ -1120,9 +1114,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1120,9 +1114,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Country.hasMany(self.Industry, {through: self.IndustryCountry}) self.Country.hasMany(self.Industry, {through: self.IndustryCountry})
self.Industry.hasMany(self.Country, {through: self.IndustryCountry}) self.Industry.hasMany(self.Country, {through: self.IndustryCountry})
async.forEach([ self.Country, self.Industry ], function(model, callback) { this.sequelize.sync({ force: true }).success(function () {
model.sync({ force: true }).done(callback)
}, function () {
async.parallel({ async.parallel({
england: function(callback) {self.Country.create({ name: 'England' }).done(callback)}, england: function(callback) {self.Country.create({ name: 'England' }).done(callback)},
france: function(callback) {self.Country.create({ name: 'France' }).done(callback)}, france: function(callback) {self.Country.create({ name: 'France' }).done(callback)},
......
...@@ -353,14 +353,12 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -353,14 +353,12 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
}) })
Project.hasOne(Task) Project.hasOne(Task)
Task.hasOne(Project) Task.belongsTo(Project)
Project.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() { self.Project = Project
self.Project = Project self.Task = Task
self.Task = Task done()
done()
})
}) })
}) })
......
...@@ -6334,10 +6334,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -6334,10 +6334,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
this.Projects.hasOne(this.Tasks, {onDelete: 'cascade', hooks: true}) this.Projects.hasOne(this.Tasks, {onDelete: 'cascade', hooks: true})
this.Tasks.belongsTo(this.Projects) this.Tasks.belongsTo(this.Projects)
this.Projects.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Tasks.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
...@@ -6733,10 +6731,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -6733,10 +6731,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
this.Projects.hasMany(this.Tasks) this.Projects.hasMany(this.Tasks)
this.Tasks.belongsTo(this.Projects) this.Tasks.belongsTo(this.Projects)
this.Projects.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Tasks.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
...@@ -6842,10 +6838,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -6842,10 +6838,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
this.Projects.hasMany(this.Tasks, {cascade: 'onDelete', joinTableName: 'projects_and_tasks', hooks: true}) this.Projects.hasMany(this.Tasks, {cascade: 'onDelete', joinTableName: 'projects_and_tasks', hooks: true})
this.Tasks.hasMany(this.Projects, {cascade: 'onDelete', joinTableName: 'projects_and_tasks', hooks: true}) this.Tasks.hasMany(this.Projects, {cascade: 'onDelete', joinTableName: 'projects_and_tasks', hooks: true})
this.Projects.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Tasks.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
...@@ -6952,10 +6946,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -6952,10 +6946,8 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
this.Projects.hasMany(this.Tasks, {hooks: true}) this.Projects.hasMany(this.Tasks, {hooks: true})
this.Tasks.hasMany(this.Projects, {hooks: true}) this.Tasks.hasMany(this.Projects, {hooks: true})
this.Projects.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Tasks.sync({ force: true }).success(function() { done()
done()
})
}) })
}) })
......
...@@ -57,8 +57,8 @@ if (Support.dialectIsMySQL()) { ...@@ -57,8 +57,8 @@ if (Support.dialectIsMySQL()) {
this.users = null this.users = null
this.tasks = null this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'}) this.User.hasMany(this.Task, {as:'Tasks', through: 'UserTasks'})
this.Task.hasMany(this.User, {as:'Users'}) this.Task.hasMany(this.User, {as:'Users', through: 'UserTasks'})
var self = this var self = this
, users = [] , users = []
...@@ -72,12 +72,10 @@ if (Support.dialectIsMySQL()) { ...@@ -72,12 +72,10 @@ if (Support.dialectIsMySQL()) {
tasks[tasks.length] = {name: 'Task' + Math.random()} tasks[tasks.length] = {name: 'Task' + Math.random()}
} }
this.User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() { self.User.bulkCreate(users).success(function() {
self.User.bulkCreate(users).success(function() { self.Task.bulkCreate(tasks).success(function() {
self.Task.bulkCreate(tasks).success(function() { done()
done()
})
}) })
}) })
}) })
......
...@@ -63,8 +63,8 @@ if (dialect.match(/^postgres/)) { ...@@ -63,8 +63,8 @@ if (dialect.match(/^postgres/)) {
this.users = null this.users = null
this.tasks = null this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'}) this.User.hasMany(this.Task, {as:'Tasks', through: 'usertasks'})
this.Task.hasMany(this.User, {as:'Users'}) this.Task.hasMany(this.User, {as:'Users', through: 'usertasks'})
var self = this var self = this
, users = [] , users = []
...@@ -78,16 +78,14 @@ if (dialect.match(/^postgres/)) { ...@@ -78,16 +78,14 @@ if (dialect.match(/^postgres/)) {
tasks[tasks.length] = {name: 'Task' + Math.random()} tasks[tasks.length] = {name: 'Task' + Math.random()}
} }
self.User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() { self.User.bulkCreate(users).success(function() {
self.User.bulkCreate(users).success(function() { self.Task.bulkCreate(tasks).success(function() {
self.Task.bulkCreate(tasks).success(function() { self.User.all().success(function(_users) {
self.User.all().success(function(_users) { self.Task.all().success(function(_tasks) {
self.Task.all().success(function(_tasks) { self.user = _users[0]
self.user = _users[0] self.task = _tasks[0]
self.task = _tasks[0] done()
done()
})
}) })
}) })
}) })
...@@ -122,8 +120,8 @@ if (dialect.match(/^postgres/)) { ...@@ -122,8 +120,8 @@ if (dialect.match(/^postgres/)) {
this.users = null this.users = null
this.tasks = null this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'}) this.User.hasMany(this.Task, {as:'Tasks', through: 'usertasks'})
this.Task.hasMany(this.User, {as:'Users'}) this.Task.hasMany(this.User, {as:'Users', through: 'usertasks'})
for (var i = 0; i < 5; ++i) { for (var i = 0; i < 5; ++i) {
users[users.length] = {id: i+1, name: 'User' + Math.random()} users[users.length] = {id: i+1, name: 'User' + Math.random()}
...@@ -133,29 +131,27 @@ if (dialect.match(/^postgres/)) { ...@@ -133,29 +131,27 @@ if (dialect.match(/^postgres/)) {
tasks[tasks.length] = {id: x+1, name: 'Task' + Math.random()} tasks[tasks.length] = {id: x+1, name: 'Task' + Math.random()}
} }
self.User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() { self.User.bulkCreate(users).done(function(err) {
self.User.bulkCreate(users).done(function(err) { expect(err).not.to.be.ok
self.Task.bulkCreate(tasks).done(function(err) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
self.Task.bulkCreate(tasks).done(function(err) { self.User.all().success(function(_users) {
expect(err).not.to.be.ok self.Task.all().success(function(_tasks) {
self.User.all().success(function(_users) { self.user = _users[0]
self.Task.all().success(function(_tasks) { self.task = _tasks[0]
self.user = _users[0] self.users = _users
self.task = _tasks[0] self.tasks = _tasks
self.users = _users
self.tasks = _tasks self.user.getTasks().on('success', function(__tasks) {
expect(__tasks).to.have.length(0)
self.user.getTasks().on('success', function(__tasks) { self.user.setTasks(self.tasks).on('success', function() {
expect(__tasks).to.have.length(0) self.user.getTasks().on('success', function(_tasks) {
self.user.setTasks(self.tasks).on('success', function() { expect(_tasks).to.have.length(self.tasks.length)
self.user.getTasks().on('success', function(_tasks) { self.user.removeTask(self.tasks[0]).on('success', function() {
expect(_tasks).to.have.length(self.tasks.length) self.user.getTasks().on('success', function(_tasks) {
self.user.removeTask(self.tasks[0]).on('success', function() { expect(_tasks).to.have.length(self.tasks.length - 1)
self.user.getTasks().on('success', function(_tasks) { done()
expect(_tasks).to.have.length(self.tasks.length - 1)
done()
})
}) })
}) })
}) })
......
...@@ -7,84 +7,87 @@ var chai = require('chai') ...@@ -7,84 +7,87 @@ var chai = require('chai')
, exec = require('child_process').exec , exec = require('child_process').exec
, version = (require(__dirname + '/../package.json')).version , version = (require(__dirname + '/../package.json')).version
, path = require('path') , path = require('path')
, os = require('os')
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Executable"), function() { if (os.type().toLowerCase().indexOf('windows') === -1) {
describe('call without arguments', function() { describe(Support.getTestDialectTeaser("Executable"), function() {
it("prints usage instructions", function(done) { describe('call without arguments', function() {
exec('bin/sequelize', function(err, stdout, stderr) { it("prints usage instructions", function(done) {
expect(stdout).to.include("No action specified. Try \"sequelize --help\" for usage information.") exec('bin/sequelize', function(err, stdout, stderr) {
done() expect(stdout).to.include("No action specified. Try \"sequelize --help\" for usage information.")
done()
})
}) })
}) })
})
;(function(flags) { ;(function(flags) {
flags.forEach(function(flag) { flags.forEach(function(flag) {
describe(flag, function() { describe(flag, function() {
it("prints the help", function(done) { it("prints the help", function(done) {
exec("bin/sequelize " + flag, function(err, stdout, stderr) { exec("bin/sequelize " + flag, function(err, stdout, stderr) {
expect(stdout).to.include("Usage: sequelize [options]") expect(stdout).to.include("Usage: sequelize [options]")
done() done()
})
}) })
}) })
}) })
}) })(["--help", "-h"])
})(["--help", "-h"])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { describe(flag, function() {
describe(flag, function() { it("prints the help", function(done) {
it("prints the help", function(done) { exec("bin/sequelize " + flag, function(err, stdout, stderr) {
exec("bin/sequelize " + flag, function(err, stdout, stderr) { expect(version).to.not.be.empty
expect(version).to.not.be.empty expect(stdout).to.include(version)
expect(stdout).to.include(version) done()
done() })
}) })
}) })
}) })
}) })(['--version', '-V'])
})(['--version', '-V'])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { describe(flag, function() {
describe(flag, function() { ;(function(folders) {
;(function(folders) { folders.forEach(function(folder) {
folders.forEach(function(folder) { it("creates a '" + folder + "' folder", function(done) {
it("creates a '" + folder + "' folder", function(done) { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() { exec("ls -ila", { cwd: __dirname + '/tmp' }, function(err, stdout) {
exec("ls -ila", { cwd: __dirname + '/tmp' }, function(err, stdout) { expect(stdout).to.include(folder)
expect(stdout).to.include(folder) done()
done() })
}) })
}) })
}) })
}) })
}) })(['config', 'migrations'])
})(['config', 'migrations'])
it("creates a config.json file", function(done) { it("creates a config.json file", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("ls -ila config", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("ls -ila config", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('config.json') expect(stdout).to.include('config.json')
done() done()
})
}) })
}) })
}) })
})
it("does not overwrite an existing config.json file", function(done) { it("does not overwrite an existing config.json file", function(done) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("echo 'foo' > config/config.json", { cwd: __dirname + '/tmp' }, function() { exec("echo 'foo' > config/config.json", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(err) { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(err) {
expect(err.code).to.equal(1) expect(err.code).to.equal(1)
exec("cat config/config.json", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("cat config/config.json", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.equal("foo\n") expect(stdout).to.equal("foo\n")
done() done()
})
}) })
}) })
}) })
...@@ -92,345 +95,346 @@ describe(Support.getTestDialectTeaser("Executable"), function() { ...@@ -92,345 +95,346 @@ describe(Support.getTestDialectTeaser("Executable"), function() {
}) })
}) })
}) })
}) })(['--init', '-i'])
})(['--init', '-i'])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { var prepare = function(callback) {
var prepare = function(callback) { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize " + flag + " 'foo'", { cwd: __dirname + '/tmp' }, callback)
exec("../../bin/sequelize " + flag + " 'foo'", { cwd: __dirname + '/tmp' }, callback) })
}) })
}) }
}
describe(flag, function() { describe(flag, function() {
it("creates a new file with the current timestamp", function(done) { it("creates a new file with the current timestamp", function(done) {
prepare(function() { prepare(function() {
exec("ls -1 migrations", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("ls -1 migrations", { cwd: __dirname + '/tmp' }, function(err, stdout) {
var date = new Date() var date = new Date()
, format = function(i) { return (parseInt(i, 10) < 10 ? '0' + i : i) } , format = function(i) { return (parseInt(i, 10) < 10 ? '0' + i : i) }
, sDate = [date.getFullYear(), format(date.getMonth() + 1), format(date.getDate()), format(date.getHours()), format(date.getMinutes())].join('') , sDate = [date.getFullYear(), format(date.getMonth() + 1), format(date.getDate()), format(date.getHours()), format(date.getMinutes())].join('')
expect(stdout).to.match(new RegExp(sDate + "..-foo.js")) expect(stdout).to.match(new RegExp(sDate + "..-foo.js"))
done() done()
})
}) })
}) })
})
it("adds a skeleton with an up and a down method", function(done) { it("adds a skeleton with an up and a down method", function(done) {
prepare(function() { prepare(function() {
exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('up: function(migration, DataTypes, done) {') expect(stdout).to.include('up: function(migration, DataTypes, done) {')
expect(stdout).to.include('down: function(migration, DataTypes, done) {') expect(stdout).to.include('down: function(migration, DataTypes, done) {')
done() done()
})
}) })
}) })
})
it("calls the done callback", function(done) { it("calls the done callback", function(done) {
prepare(function() { prepare(function() {
exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("cat migrations/*-foo.js", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('done()') expect(stdout).to.include('done()')
expect(stdout.match(/(done\(\))/)).to.have.length(2) expect(stdout.match(/(done\(\))/)).to.have.length(2)
done() done()
})
}) })
}) })
}) })
}) })
}) })(['--create-migration', '-c'])
})(['--create-migration', '-c'])
;(function(flags) { ;(function(flags) {
flags.forEach(function(flag) { flags.forEach(function(flag) {
var prepare = function(callback) { var prepare = function(callback) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function() {
exec("../../bin/sequelize " + flag + " 'foo'", { cwd: __dirname + '/tmp' }, callback) exec("../../bin/sequelize " + flag + " 'foo'", { cwd: __dirname + '/tmp' }, callback)
})
}) })
}) }
}
describe(flag, function() { describe(flag, function() {
it("creates a new file with the current timestamp", function(done) { it("creates a new file with the current timestamp", function(done) {
prepare(function() { prepare(function() {
exec("ls -1 migrations", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("ls -1 migrations", { cwd: __dirname + '/tmp' }, function(err, stdout) {
var date = new Date() var date = new Date()
, format = function(i) { return (parseInt(i, 10) < 10 ? '0' + i : i) } , format = function(i) { return (parseInt(i, 10) < 10 ? '0' + i : i) }
, sDate = [date.getFullYear(), format(date.getMonth() + 1), format(date.getDate()), format(date.getHours()), format(date.getMinutes())].join('') , sDate = [date.getFullYear(), format(date.getMonth() + 1), format(date.getDate()), format(date.getHours()), format(date.getMinutes())].join('')
expect(stdout).to.match(new RegExp(sDate + "..-foo.coffee")) expect(stdout).to.match(new RegExp(sDate + "..-foo.coffee"))
done() done()
})
}) })
}) })
})
it("adds a skeleton with an up and a down method", function(done) { it("adds a skeleton with an up and a down method", function(done) {
prepare(function() { prepare(function() {
exec("cat migrations/*-foo.coffee", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("cat migrations/*-foo.coffee", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('up: (migration, DataTypes, done) ->') expect(stdout).to.include('up: (migration, DataTypes, done) ->')
expect(stdout).to.include('down: (migration, DataTypes, done) ->') expect(stdout).to.include('down: (migration, DataTypes, done) ->')
done() done()
})
}) })
}) })
})
it("calls the done callback", function(done) { it("calls the done callback", function(done) {
prepare(function() { prepare(function() {
exec("cat migrations/*-foo.coffee", { cwd: __dirname + '/tmp' }, function(err, stdout) { exec("cat migrations/*-foo.coffee", { cwd: __dirname + '/tmp' }, function(err, stdout) {
expect(stdout).to.include('done()') expect(stdout).to.include('done()')
expect(stdout.match(/(done\(\))/)).to.have.length(2) expect(stdout.match(/(done\(\))/)).to.have.length(2)
done() done()
})
}) })
}) })
}) })
}) })
}) })(['--coffee --create-migration', '--coffee -c'])
})(['--coffee --create-migration', '--coffee -c'])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { var prepare = function(callback) {
var prepare = function(callback) { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) { var source = (flag.indexOf('coffee') === -1)
var source = (flag.indexOf('coffee') === -1) ? "../assets/migrations/*-createPerson.js"
? "../assets/migrations/*-createPerson.js" : "../assets/migrations/*-createPerson.coffee"
: "../assets/migrations/*-createPerson.coffee"
exec("cp " + source + " ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cp " + source + " ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) { var dialect = Support.getTestDialect()
var dialect = Support.getTestDialect() , config = require(__dirname + '/config/config.js')
, config = require(__dirname + '/config/config.js')
config.sqlite.storage = __dirname + "/tmp/test.sqlite"
config.sqlite.storage = __dirname + "/tmp/test.sqlite" config = _.extend(config, config[dialect], { dialect: dialect })
config = _.extend(config, config[dialect], { dialect: dialect })
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize " + flag, { cwd: __dirname + "/tmp" }, callback)
exec("../../bin/sequelize " + flag, { cwd: __dirname + "/tmp" }, callback) })
}) })
}) })
}) })
}) })
}) }
}
describe(flag, function() { describe(flag, function() {
it("creates a SequelizeMeta table", function(done) { it("creates a SequelizeMeta table", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(2) expect(tables).to.have.length(2)
expect(tables[1]).to.equal("SequelizeMeta") expect(tables[1]).to.equal("SequelizeMeta")
done() done()
}) })
}.bind(this)) }.bind(this))
}) })
it("creates the respective table", function(done) { it("creates the respective table", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(2) expect(tables).to.have.length(2)
expect(tables[0]).to.equal("Person") expect(tables[0]).to.equal("Person")
done() done()
}) })
}.bind(this)) }.bind(this))
})
}) })
}) })
}) })([
})([ '--migrate',
'--migrate', '--migrate --coffee',
'--migrate --coffee', '--migrate --config ../tmp/config/config.json',
'--migrate --config ../tmp/config/config.json', '--migrate --config ' + path.join(__dirname, 'tmp', 'config', 'config.json'),
'--migrate --config ' + path.join(__dirname, 'tmp', 'config', 'config.json'), '-m',
'-m', '-m --coffee',
'-m --coffee', '-m --config ../tmp/config/config.json',
'-m --config ../tmp/config/config.json', '-m --config ' + path.join(__dirname, 'tmp', 'config', 'config.json')
'-m --config ' + path.join(__dirname, 'tmp', 'config', 'config.json') ])
])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { var execBinary = function(callback, _flag) {
var execBinary = function(callback, _flag) { _flag = _flag || flag
_flag = _flag || flag
var dialect = Support.getTestDialect()
var dialect = Support.getTestDialect() , config = require(__dirname + '/config/config.js')
, config = require(__dirname + '/config/config.js')
config.sqlite.storage = __dirname + "/tmp/test.sqlite"
config.sqlite.storage = __dirname + "/tmp/test.sqlite" config = _.extend(config, config[dialect], { dialect: dialect })
config = _.extend(config, config[dialect], { dialect: dialect })
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize " + _flag, { cwd: __dirname + "/tmp" }, callback)
exec("../../bin/sequelize " + _flag, { cwd: __dirname + "/tmp" }, callback) })
}) }
}
var prepare = function(callback, options) {
var prepare = function(callback, options) { options = options || {}
options = options || {}
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) { if (!options.skipExecBinary) {
if (!options.skipExecBinary) { execBinary(callback, options.flag)
execBinary(callback, options.flag) }
} })
}) })
}) })
}) })
}) }
}
describe(flag, function() { describe(flag, function() {
it("creates a SequelizeMeta table", function(done) { it("creates a SequelizeMeta table", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(1) expect(tables).to.have.length(1)
expect(tables[0]).to.equal("SequelizeMeta") expect(tables[0]).to.equal("SequelizeMeta")
done() done()
}) })
}.bind(this)) }.bind(this))
}) })
it("stops execution if no migrations have been done yet", function(done) { it("stops execution if no migrations have been done yet", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function(err, output) { prepare(function(err, output) {
expect(err).to.be.null expect(err).to.be.null
expect(output).to.include("There are no pending migrations.") expect(output).to.include("There are no pending migrations.")
done() done()
}.bind(this)) }.bind(this))
}) })
it("is correctly undoing a migration if they have been done yet", function(done) { it("is correctly undoing a migration if they have been done yet", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(2) expect(tables).to.have.length(2)
expect(tables[0]).to.equal("Person") expect(tables[0]).to.equal("Person")
execBinary(function(err, output) { execBinary(function(err, output) {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
expect(tables).to.have.length(1) expect(tables).to.have.length(1)
expect(tables[0]).to.equal("SequelizeMeta") expect(tables[0]).to.equal("SequelizeMeta")
done() done()
})
}) })
}) })
}) }.bind(this), { flag: '-m' })
}.bind(this), { flag: '-m' }) })
}) })
}) })
}) })(['--migrate --undo', '-mu', '--undo', '-u'])
})(['--migrate --undo', '-mu', '--undo', '-u'])
;(function(flags) {
;(function(flags) { flags.forEach(function(flag) {
flags.forEach(function(flag) { var prepare = function(callback) {
var prepare = function(callback) { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) { var dialect = Support.getTestDialect()
var dialect = Support.getTestDialect() , config = require(__dirname + '/config/config.js')
, config = require(__dirname + '/config/config.js')
config.sqlite.storage = __dirname + "/tmp/test.sqlite"
config.sqlite.storage = __dirname + "/tmp/test.sqlite" config = _.extend(config, config[dialect], { dialect: dialect })
config = _.extend(config, config[dialect], { dialect: dialect }) var url = Support.getTestUrl(config);
var url = Support.getTestUrl(config); exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize -m " + flag + " " + url, { cwd: __dirname + "/tmp" }, callback)
exec("../../bin/sequelize -m " + flag + " " + url, { cwd: __dirname + "/tmp" }, callback) })
}) })
}) })
}) })
}) })
}) }
}
describe(flag, function() { describe(flag, function() {
it("creates a SequelizeMeta table", function(done) { it("creates a SequelizeMeta table", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(2) expect(tables).to.have.length(2)
expect(tables[1]).to.equal("SequelizeMeta") expect(tables[1]).to.equal("SequelizeMeta")
done() done()
}) })
}.bind(this)) }.bind(this))
}) })
it("creates the respective table via url", function(done) { it("creates the respective table via url", function(done) {
var sequelize = this.sequelize var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite" options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options) sequelize = new Support.Sequelize("", "", "", options)
} }
prepare(function() { prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) { sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort() tables = tables.sort()
expect(tables).to.have.length(2) expect(tables).to.have.length(2)
expect(tables[0]).to.equal("Person") expect(tables[0]).to.equal("Person")
done() done()
}) })
}.bind(this)) }.bind(this))
})
}) })
}) })
}) })(['--url', '-U'])
})(['--url', '-U']) })
})
}
var fs = require('fs') var fs = require('fs')
, path = require('path') , path = require('path')
, _ = require('lodash')
, Sequelize = require(__dirname + "/../index") , Sequelize = require(__dirname + "/../index")
, DataTypes = require(__dirname + "/../lib/data-types") , DataTypes = require(__dirname + "/../lib/data-types")
, Config = require(__dirname + "/config/config") , Config = require(__dirname + "/config/config")
...@@ -44,25 +45,14 @@ var Support = { ...@@ -44,25 +45,14 @@ var Support = {
var config = Config[options.dialect] var config = Config[options.dialect]
options.logging = (options.hasOwnProperty('logging') ? options.logging : false) var sequelizeOptions = _.defaults(options, {
options.pool = options.pool !== undefined ? options.pool : config.pool
var sequelizeOptions = {
host: options.host || config.host, host: options.host || config.host,
logging: options.logging, logging: false,
dialect: options.dialect, dialect: options.dialect,
port: options.port || process.env.SEQ_PORT || config.port, port: options.port || process.env.SEQ_PORT || config.port,
pool: options.pool, pool: config.pool,
dialectOptions: options.dialectOptions || {} dialectOptions: options.dialectOptions || {}
} })
if (!!options.define) {
sequelizeOptions.define = options.define
}
if (!!config.storage) {
sequelizeOptions.storage = config.storage
}
if (process.env.DIALECT === 'postgres-native') { if (process.env.DIALECT === 'postgres-native') {
sequelizeOptions.native = true sequelizeOptions.native = true
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!