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

Commit 3ed27be1 by Sascha Depold

Merge branch 'master' into features/first_steps_for_syntax_sugar

Conflicts:
	package.json
	spec/dao-factory.spec.js
2 parents 551c4e7e 95a23b6d
Showing with 6467 additions and 7632 deletions
...@@ -3,7 +3,7 @@ before_script: ...@@ -3,7 +3,7 @@ before_script:
- "psql -c 'create database sequelize_test;' -U postgres" - "psql -c 'create database sequelize_test;' -U postgres"
script: script:
- "npm run test-buster-travis" - "make test"
notifications: notifications:
email: email:
......
REPORTER ?= dot
TESTS = $(shell find ./test/* -name "*.test.js")
DIALECT ?= mysql
# test commands
teaser:
@echo "" && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo '# Running tests for $(DIALECT) #' && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo ''
test:
@make teaser && \
./node_modules/mocha/bin/mocha \
--colors \
--reporter $(REPORTER) \
$(TESTS)
sqlite:
@DIALECT=sqlite make test
mysql:
@DIALECT=mysql make test
postgres:
@DIALECT=postgres make test
postgres-native:
@DIALECT=postgres-native make test
# test aliases
pgsql: postgres
postgresn: postgres-native
# test all the dialects \o/
all: sqlite mysql postgres postgres-native
.PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test
\ No newline at end of file
...@@ -139,30 +139,27 @@ $ npm install ...@@ -139,30 +139,27 @@ $ npm install
### 4. Run the tests ### ### 4. Run the tests ###
Right now, the test base is split into the `spec` folder (which contains the Right now, the test base is split into the `test` folder (which contains the
lovely [BusterJS](http://busterjs.org) tests). lovely [Mocha](http://visionmedia.github.io/mocha/) tests).
As you might haven't installed all of the supported SQL dialects, here is how As you might haven't installed all of the supported SQL dialects, here is how
to run the test suites for your development environment: to run the test suites for your development environment:
```console ```console
$ # run all tests at once: $ # run all tests at once:
$ npm test $ make all
$ # run all of the buster specs (for all dialects):
$ npm run test-buster
$ # run the buster specs for mysql: $ # run the buster specs for mysql:
$ npm run test-buster-mysql $ make mysql
$ # run the buster specs for sqlite: $ # run the buster specs for sqlite:
$ npm run test-buster-sqlite $ make sqlite
$ # run the buster specs for postgresql: $ # run the buster specs for postgresql:
$ npm run test-buster-postgres $ make pgsql
$ # alternatively you can pass database credentials with $variables when testing with buster.js $ # alternatively you can pass database credentials with $variables when testing
$ DIALECT=dialect SEQ_DB=database SEQ_USER=user SEQ_PW=password buster-test $ DIALECT=dialect SEQ_DB=database SEQ_USER=user SEQ_PW=password make test
``` ```
### 5. That's all ### ### 5. That's all ###
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
- [DEPENDENCIES] Upgraded validator for IPv6 support. [#603](https://github.com/sequelize/sequelize/pull/603). thanks to durango - [DEPENDENCIES] Upgraded validator for IPv6 support. [#603](https://github.com/sequelize/sequelize/pull/603). thanks to durango
- [DEPENDENCIES] replaced underscore by lodash. [#954](https://github.com/sequelize/sequelize/pull/594). thanks to durango - [DEPENDENCIES] replaced underscore by lodash. [#954](https://github.com/sequelize/sequelize/pull/594). thanks to durango
- [DEPENDENCIES] Upgraded pg to 2.0.0. [#711](https://github.com/sequelize/sequelize/pull/711). thanks to durango - [DEPENDENCIES] Upgraded pg to 2.0.0. [#711](https://github.com/sequelize/sequelize/pull/711). thanks to durango
- [DEPENDENCIES] Upgraded command to 2.0.0 and generic-pool to 2.0.4. thanks to durango
- [DEPENDENCIES] No longer require semver. thanks to durango
- [BUG] Fix string escape with postgresql on raw SQL queries. [#586](https://github.com/sequelize/sequelize/pull/586). thanks to zanamixx - [BUG] Fix string escape with postgresql on raw SQL queries. [#586](https://github.com/sequelize/sequelize/pull/586). thanks to zanamixx
- [BUG] "order by" is now after "group by". [#585](https://github.com/sequelize/sequelize/pull/585). thanks to mekanics - [BUG] "order by" is now after "group by". [#585](https://github.com/sequelize/sequelize/pull/585). thanks to mekanics
- [BUG] Added decimal support for min/max. [#583](https://github.com/sequelize/sequelize/pull/583). thanks to durango - [BUG] Added decimal support for min/max. [#583](https://github.com/sequelize/sequelize/pull/583). thanks to durango
...@@ -21,6 +23,10 @@ ...@@ -21,6 +23,10 @@
- [BUG] bulkCreate would have problems with a disparate field list [#738](https://github.com/sequelize/sequelize/pull/738). thanks to durango - [BUG] bulkCreate would have problems with a disparate field list [#738](https://github.com/sequelize/sequelize/pull/738). thanks to durango
- [BUG] Fixed problems with quoteIdentifiers and {raw: false} option on raw queries [#751](https://github.com/sequelize/sequelize/pull/751). thanks to janmeier - [BUG] Fixed problems with quoteIdentifiers and {raw: false} option on raw queries [#751](https://github.com/sequelize/sequelize/pull/751). thanks to janmeier
- [BUG] Fixed SQL escaping with sqlite and unified escaping [#700](https://github.com/sequelize/sequelize/pull/700). thanks to PiPeep - [BUG] Fixed SQL escaping with sqlite and unified escaping [#700](https://github.com/sequelize/sequelize/pull/700). thanks to PiPeep
- [BUG] Fixed Postgres' pools [ff57af63](https://github.com/sequelize/sequelize/commit/ff57af63c2eb395b4828a5984a22984acdc2a5e1)
- [BUG] Fixed BLOB/TEXT columns having a default value declared in MySQL [#793](https://github.com/sequelize/sequelize/pull/793). thanks to durango
- [BUG] You can now use .find() on any single integer primary key when throwing just a number as an argument [#796](https://github.com/sequelize/sequelize/pull/796). thanks to durango
- [BUG] Adding unique to a column for Postgres in the migrator should be fixed [#795](https://github.com/sequelize/sequelize/pull/795). thanks to durango
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango - [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango - [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude - [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
...@@ -42,8 +48,12 @@ ...@@ -42,8 +48,12 @@
- [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango - [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango
- [FEATURE] Added support for model instances being referenced [#761](https://github.com/sequelize/sequelize/pull/761) thanks to sdepold - [FEATURE] Added support for model instances being referenced [#761](https://github.com/sequelize/sequelize/pull/761) thanks to sdepold
- [FEATURE] Added support for specifying the path to load a module for a dialect. [#766](https://github.com/sequelize/sequelize/pull/766) thanks to sonnym. - [FEATURE] Added support for specifying the path to load a module for a dialect. [#766](https://github.com/sequelize/sequelize/pull/766) thanks to sonnym.
- [FEATURE] Drop index if exists has been added to sqlite [#766](https://github.com/sequelize/sequelize/pull/776) thanks to coderbuzz
- [FEATURE] bulkCreate() now has a third argument which gives you the ability to validate each row before attempting to bulkInsert [#797](https://github.com/sequelize/sequelize/pull/797). thanks to durango
- [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier - [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier - [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
- [REFACTORING] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
# v1.6.0 # # v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work - [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
...@@ -43,7 +43,7 @@ module.exports = (function() { ...@@ -43,7 +43,7 @@ module.exports = (function() {
if (obsoleteAssociations.length > 0) { if (obsoleteAssociations.length > 0) {
// clear the old associations // clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) { var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = null associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id return associatedObject.id
}) })
...@@ -55,12 +55,12 @@ module.exports = (function() { ...@@ -55,12 +55,12 @@ module.exports = (function() {
if (unassociatedObjects.length > 0) { if (unassociatedObjects.length > 0) {
// set the new associations // set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) { var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = self.instance.id associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id return associatedObject.id
}) })
update = {} update = {}
update[self.__factory.identifier] = self.instance.id update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
chainer.add(this.__factory.target.update(update, { id: unassociatedIds })) chainer.add(this.__factory.target.update(update, { id: unassociatedIds }))
} }
......
...@@ -70,17 +70,25 @@ module.exports = (function() { ...@@ -70,17 +70,25 @@ module.exports = (function() {
HasOne.prototype.injectSetter = function(obj) { HasOne.prototype.injectSetter = function(obj) {
var self = this var self = this
, options = self.options || {}
obj[this.accessors.set] = function(associatedObject) { obj[this.accessors.set] = function(associatedObject) {
var instance = this; var instance = this
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]().success(function(oldObj) { instance[self.accessors.get]().success(function(oldObj) {
if (oldObj) { if (oldObj) {
oldObj[self.identifier] = null oldObj[self.identifier] = null
oldObj.save() oldObj.save().success(function() {
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
} else {
emitter.emit('success', null)
} }
})
} else {
if (associatedObject) { if (associatedObject) {
associatedObject[self.identifier] = instance.id associatedObject[self.identifier] = instance.id
associatedObject associatedObject
...@@ -90,7 +98,7 @@ module.exports = (function() { ...@@ -90,7 +98,7 @@ module.exports = (function() {
} else { } else {
emitter.emit('success', null) emitter.emit('success', null)
} }
}
}) })
}).run() }).run()
} }
......
...@@ -277,14 +277,20 @@ module.exports = (function() { ...@@ -277,14 +277,20 @@ module.exports = (function() {
} }
var primaryKeys = this.primaryKeys var primaryKeys = this.primaryKeys
, keys = Object.keys(primaryKeys)
, keysLength = keys.length
// options is not a hash but an id // options is not a hash but an id
if (typeof options === 'number') { if (typeof options === 'number') {
options = { where: options } var oldOption = options
options = { where: {} }
if (keysLength === 1) {
options.where[keys[0]] = oldOption
} else {
options.where.id = oldOption
}
} else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) { } else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) {
var where = {} var where = {}
, self = this
, keys = Object.keys(primaryKeys)
Utils._.each(arguments, function(arg, i) { Utils._.each(arguments, function(arg, i) {
var key = keys[i] var key = keys[i]
...@@ -315,7 +321,6 @@ module.exports = (function() { ...@@ -315,7 +321,6 @@ module.exports = (function() {
this.options.whereCollection = options.where || null this.options.whereCollection = options.where || null
} else if (typeof options === "string") { } else if (typeof options === "string") {
var where = {} var where = {}
, keys = Object.keys(primaryKeys)
if (this.primaryKeyCount === 1) { if (this.primaryKeyCount === 1) {
where[primaryKeys[keys[0]]] = options; where[primaryKeys[keys[0]]] = options;
...@@ -404,7 +409,7 @@ module.exports = (function() { ...@@ -404,7 +409,7 @@ module.exports = (function() {
} }
DAOFactory.prototype.build = function(values, options) { DAOFactory.prototype.build = function(values, options) {
options = options || { isNewRecord: true } options = options || { isNewRecord: true, isDirty: true }
var self = this var self = this
, instance = new this.DAO(values, this.options, options.isNewRecord) , instance = new this.DAO(values, this.options, options.isNewRecord)
...@@ -412,6 +417,7 @@ module.exports = (function() { ...@@ -412,6 +417,7 @@ module.exports = (function() {
instance.isNewRecord = options.isNewRecord instance.isNewRecord = options.isNewRecord
instance.daoFactoryName = this.name instance.daoFactoryName = this.name
instance.daoFactory = this instance.daoFactory = this
instance.isDirty = options.isDirty
return instance return instance
} }
...@@ -461,13 +467,31 @@ module.exports = (function() { ...@@ -461,13 +467,31 @@ module.exports = (function() {
* generated IDs and other default values in a way that can be mapped to * generated IDs and other default values in a way that can be mapped to
* multiple records * multiple records
*/ */
DAOFactory.prototype.bulkCreate = function(records, fields) { DAOFactory.prototype.bulkCreate = function(records, fields, options) {
options = options || {}
options.validate = options.validate || false
fields = fields || []
var self = this var self = this
, daos = records.map(function(v) { return self.build(v) })
, updatedAtAttr = self.options.underscored ? 'updated_at' : 'updatedAt' , updatedAtAttr = self.options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = self.options.underscored ? 'created_at' : 'createdAt' , createdAtAttr = self.options.underscored ? 'created_at' : 'createdAt'
, errors = []
, daos = records.map(function(v) {
var build = self.build(v)
if (options.validate === true) {
var valid = build.validate({skip: fields})
if (valid !== null) {
errors[errors.length] = {record: v, errors: valid}
}
}
return build
})
fields = fields || [] if (options.validate === true && errors.length > 0) {
return new Utils.CustomEventEmitter(function(emitter) {
emitter.emit('error', errors)
}).run()
}
// we will re-create from DAOs, which may have set up default attributes // we will re-create from DAOs, which may have set up default attributes
records = [] records = []
...@@ -485,7 +509,7 @@ module.exports = (function() { ...@@ -485,7 +509,7 @@ module.exports = (function() {
values[updatedAtAttr] = Utils.now() values[updatedAtAttr] = Utils.now()
} }
records.push(values); records.push(values)
}) })
// Validate enums // Validate enums
......
var Validator = require("validator") var Validator = require("validator")
, Utils = require("./utils") , Utils = require("./utils")
var DaoValidator = module.exports = function(model) { var DaoValidator = module.exports = function(model, options) {
options = options || {}
options.skip = options.skip || []
this.model = model this.model = model
this.options = options
} }
DaoValidator.prototype.validate = function() { DaoValidator.prototype.validate = function() {
...@@ -32,17 +36,19 @@ var validateModel = function() { ...@@ -32,17 +36,19 @@ var validateModel = function() {
} }
var validateAttributes = function() { var validateAttributes = function() {
var errors = {} var self = this
, errors = {}
// for each field and value // for each field and value
Utils._.each(this.model.dataValues, function(value, field) { Utils._.each(this.model.dataValues, function(value, field) {
var rawAttribute = this.model.rawAttributes[field] var rawAttribute = self.model.rawAttributes[field]
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined))) , hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1
if (this.model.validators.hasOwnProperty(field) && !hasAllowedNull) { if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) {
errors = Utils._.merge(errors, validateAttribute.call(this, value, field)) errors = Utils._.merge(errors, validateAttribute.call(self, value, field))
} }
}.bind(this)) // for each field })
return errors return errors
} }
......
...@@ -84,10 +84,15 @@ module.exports = (function() { ...@@ -84,10 +84,15 @@ module.exports = (function() {
DAO.prototype.getDataValue = function(name) { DAO.prototype.getDataValue = function(name) {
return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name] return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name]
} }
DAO.prototype.get = DAO.prototype.getDataValue
DAO.prototype.setDataValue = function(name, value) { DAO.prototype.setDataValue = function(name, value) {
if (Utils.hasChanged(this.dataValues[name], value)) {
this.isDirty = true
}
this.dataValues[name] = value this.dataValues[name] = value
} }
DAO.prototype.set = DAO.prototype.setDataValue
// if an array with field names is passed to save() // if an array with field names is passed to save()
// only those fields will be updated // only those fields will be updated
...@@ -120,10 +125,24 @@ module.exports = (function() { ...@@ -120,10 +125,24 @@ module.exports = (function() {
for (var attrName in this.daoFactory.rawAttributes) { for (var attrName in this.daoFactory.rawAttributes) {
if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) { if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) {
var definition = this.daoFactory.rawAttributes[attrName] var definition = this.daoFactory.rawAttributes[attrName]
, isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString())) , isEnum = definition.type && (definition.type.toString() === DataTypes.ENUM.toString())
, isHstore = (!!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type) , isHstore = !!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type
, hasValue = (typeof values[attrName] !== 'undefined') , hasValue = values[attrName] !== undefined
, valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) , isMySQL = this.daoFactory.daoFactoryManager.sequelize.options.dialect === "mysql"
, ciCollation = !!this.daoFactory.options.collate && this.daoFactory.options.collate.match(/_ci$/i)
, valueOutOfScope
if (isEnum && isMySQL && ciCollation && hasValue) {
var scopeIndex = (definition.values || []).map(function(d) { return d.toLowerCase() }).indexOf(values[attrName].toLowerCase())
valueOutOfScope = scopeIndex === -1
// We'll return what the actual case will be, since a simple SELECT query would do the same...
if (!valueOutOfScope) {
values[attrName] = definition.values[scopeIndex]
}
} else {
valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
}
if (isEnum && hasValue && valueOutOfScope) { if (isEnum && hasValue && valueOutOfScope) {
throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', '))
...@@ -149,6 +168,7 @@ module.exports = (function() { ...@@ -149,6 +168,7 @@ module.exports = (function() {
}).run() }).run()
} }
else if (this.isNewRecord) { else if (this.isNewRecord) {
this.isDirty = false
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
} else { } else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id }; var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id };
...@@ -157,6 +177,7 @@ module.exports = (function() { ...@@ -157,6 +177,7 @@ module.exports = (function() {
identifier = this.__options.whereCollection; identifier = this.__options.whereCollection;
} }
this.isDirty = false
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier) , query = this.QueryInterface.update(this, tableName, values, identifier)
...@@ -191,6 +212,7 @@ module.exports = (function() { ...@@ -191,6 +212,7 @@ module.exports = (function() {
this[valueName] = obj.values[valueName] this[valueName] = obj.values[valueName]
} }
} }
this.isDirty = false
emitter.emit('success', this) emitter.emit('success', this)
}.bind(this)) }.bind(this))
}.bind(this)).run() }.bind(this)).run()
...@@ -201,8 +223,8 @@ module.exports = (function() { ...@@ -201,8 +223,8 @@ module.exports = (function() {
* *
* @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries. * @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/ */
DAO.prototype.validate = function() { DAO.prototype.validate = function(object) {
var validator = new DaoValidator(this) var validator = new DaoValidator(this, object)
, errors = validator.validate() , errors = validator.validate()
return (Utils._.isEmpty(errors) ? null : errors) return (Utils._.isEmpty(errors) ? null : errors)
...@@ -224,14 +246,21 @@ module.exports = (function() { ...@@ -224,14 +246,21 @@ module.exports = (function() {
readOnlyAttributes.push('updatedAt') readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt') readOnlyAttributes.push('deletedAt')
var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) { Utils._.each(updates, function(value, attr) {
var updateAllowed = ( var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) && (readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) && (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1) (self.attributes.indexOf(attr) > -1)
) )
updateAllowed && (self[attr] = value) if (updateAllowed) {
if (Utils.hasChanged(self[attr], value)) {
isDirty = true
}
self[attr] = value
}
}) })
this.isDirty = isDirty
} }
DAO.prototype.destroy = function() { DAO.prototype.destroy = function() {
...@@ -330,7 +359,19 @@ module.exports = (function() { ...@@ -330,7 +359,19 @@ module.exports = (function() {
// (the same is true for __defineSetter and 'prototype' getters) // (the same is true for __defineSetter and 'prototype' getters)
if (has !== true) { if (has !== true) {
this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; }); this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; });
this.__defineSetter__(attribute, has.set || function(v) { this.dataValues[attribute] = v; }); this.__defineSetter__(attribute, has.set || function(v) {
if (Utils.hasChanged(this.dataValues[attribute], v)) {
//Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated
var updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt'
, touchedAtAttr = this.__options.underscored ? 'touched_at' : 'touchedAt'
if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) {
this.isDirty = true
}
}
this.dataValues[attribute] = v
});
} }
this[attribute] = value; this[attribute] = value;
......
...@@ -262,7 +262,7 @@ module.exports = (function() { ...@@ -262,7 +262,7 @@ module.exports = (function() {
result = transformRowsWithEagerLoadingIntoDaos.call(this, results) result = transformRowsWithEagerLoadingIntoDaos.call(this, results)
} else { } else {
result = results.map(function(result) { result = results.map(function(result) {
return this.callee.build(result, { isNewRecord: false }) return this.callee.build(result, { isNewRecord: false, isDirty: false })
}.bind(this)) }.bind(this))
} }
...@@ -287,7 +287,7 @@ module.exports = (function() { ...@@ -287,7 +287,7 @@ module.exports = (function() {
var transformRowWithEagerLoadingIntoDao = function(result, dao) { var transformRowWithEagerLoadingIntoDao = function(result, dao) {
// let's build the actual dao instance first... // let's build the actual dao instance first...
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false }) dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false, isDirty: false })
// ... and afterwards the prefetched associations // ... and afterwards the prefetched associations
for (var tableName in result) { for (var tableName in result) {
...@@ -323,7 +323,7 @@ module.exports = (function() { ...@@ -323,7 +323,7 @@ module.exports = (function() {
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1) accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
associationData.forEach(function(data) { associationData.forEach(function(data) {
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false }) var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false, isDirty: false })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) , isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) { if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
......
...@@ -82,7 +82,9 @@ module.exports = (function() { ...@@ -82,7 +82,9 @@ module.exports = (function() {
read: Pooling.Pool({ read: Pooling.Pool({
name: 'sequelize-read', name: 'sequelize-read',
create: function (done) { create: function (done) {
if (reads >= self.config.replication.read.length) reads = 0; if (reads >= self.config.replication.read.length) {
reads = 0
}
var config = self.config.replication.read[reads++]; var config = self.config.replication.read[reads++];
connect.call(self, function (err, connection) { connect.call(self, function (err, connection) {
...@@ -168,11 +170,12 @@ module.exports = (function() { ...@@ -168,11 +170,12 @@ module.exports = (function() {
query.done(function() { query.done(function() {
self.pendingQueries--; self.pendingQueries--;
if (self.pool) self.pool.release(query.client); if (self.pool) {
else { self.pool.release(query.client);
} else {
if (self.pendingQueries === 0) { if (self.pendingQueries === 0) {
setTimeout(function() { setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self); self.pendingQueries === 0 && self.disconnect.call(self)
}, 100); }, 100);
} }
} }
...@@ -180,16 +183,16 @@ module.exports = (function() { ...@@ -180,16 +183,16 @@ module.exports = (function() {
if (!this.pool) { if (!this.pool) {
query.run(sql); query.run(sql);
} } else {
else {
this.pool.acquire(function(err, client) { this.pool.acquire(function(err, client) {
if (err) return query.emit('error', err); if (err) {
return query.emit('error', err)
}
query.client = client; query.client = client
query.run(sql); query.run(sql)
return; return;
}, undefined, options.type); }, undefined, options.type)
} }
return query; return query;
...@@ -209,8 +212,10 @@ module.exports = (function() { ...@@ -209,8 +212,10 @@ module.exports = (function() {
}; };
ConnectorManager.prototype.disconnect = function() { ConnectorManager.prototype.disconnect = function() {
if (this.client) disconnect.call(this, this.client); if (this.client) {
return; disconnect.call(this, this.client)
}
return
}; };
...@@ -265,12 +270,12 @@ module.exports = (function() { ...@@ -265,12 +270,12 @@ module.exports = (function() {
switch(err.code) { switch(err.code) {
case 'ECONNREFUSED': case 'ECONNREFUSED':
case 'ER_ACCESS_DENIED_ERROR': case 'ER_ACCESS_DENIED_ERROR':
emitter.emit('error', new Error("Failed to authenticate for MySQL. Please double check your settings.")) emitter.emit('error', 'Failed to authenticate for MySQL. Please double check your settings.')
break break
case 'ENOTFOUND': case 'ENOTFOUND':
case 'EHOSTUNREACH': case 'EHOSTUNREACH':
case 'EINVAL': case 'EINVAL':
emitter.emit('error', new Error("Failed to find MySQL server. Please double check your settings.")) emitter.emit('error', 'Failed to find MySQL server. Please double check your settings.')
break break
} }
} }
...@@ -295,7 +300,7 @@ module.exports = (function() { ...@@ -295,7 +300,7 @@ module.exports = (function() {
} }
var validateConnection = function(client) { var validateConnection = function(client) {
return client && client.state != 'disconnected' return client && client.state !== 'disconnected'
} }
var enqueue = function(queueItem, options) { var enqueue = function(queueItem, options) {
...@@ -342,8 +347,6 @@ module.exports = (function() { ...@@ -342,8 +347,6 @@ module.exports = (function() {
} }
var afterQuery = function(queueItem) { var afterQuery = function(queueItem) {
var self = this
dequeue.call(this, queueItem) dequeue.call(this, queueItem)
transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length) transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length)
disconnectIfNoConnections.call(this) disconnectIfNoConnections.call(this)
......
...@@ -29,9 +29,8 @@ module.exports = (function() { ...@@ -29,9 +29,8 @@ module.exports = (function() {
return Utils._.template(query)({}) return Utils._.template(query)({})
}, },
dropSchema: function() { dropSchema: function(tableName, options) {
var query = "SHOW TABLES" return QueryGenerator.dropTableQuery(tableName, options)
return Utils._.template(query)({})
}, },
showSchemasQuery: function() { showSchemasQuery: function() {
...@@ -377,7 +376,7 @@ module.exports = (function() { ...@@ -377,7 +376,7 @@ module.exports = (function() {
}, options || {}) }, options || {})
return Utils._.compact([ return Utils._.compact([
"CREATE", options.indicesType, "INDEX IF NOT EXISTS", options.indexName, "CREATE", options.indicesType, "INDEX", options.indexName,
(options.indexType ? ('USING ' + options.indexType) : undefined), (options.indexType ? ('USING ' + options.indexType) : undefined),
"ON", tableName, '(' + transformedAttributes.join(', ') + ')', "ON", tableName, '(' + transformedAttributes.join(', ') + ')',
(options.parser ? "WITH PARSER " + options.parser : undefined) (options.parser ? "WITH PARSER " + options.parser : undefined)
...@@ -521,7 +520,8 @@ module.exports = (function() { ...@@ -521,7 +520,8 @@ module.exports = (function() {
template += " auto_increment" template += " auto_increment"
} }
if ((dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) { // Blobs/texts cannot have a defaultValue
if (dataType.type !== "TEXT" && dataType.type._binary !== true && (dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) {
template += " DEFAULT " + this.escape(dataType.defaultValue) template += " DEFAULT " + this.escape(dataType.defaultValue)
} }
......
...@@ -9,8 +9,9 @@ module.exports = (function() { ...@@ -9,8 +9,9 @@ module.exports = (function() {
this.client = null this.client = null
this.config = config || {} this.config = config || {}
this.config.port = this.config.port || 5432 this.config.port = this.config.port || 5432
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0)) this.pooling = (!!this.config.pool && (this.config.pool.maxConnections > 0))
this.pg = this.config.native ? require(pgModule).native : require(pgModule) this.pg = this.config.native ? require(pgModule).native : require(pgModule)
this.poolIdentifier = null
// Better support for BigInts // Better support for BigInts
// https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935 // https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935
...@@ -18,43 +19,62 @@ module.exports = (function() { ...@@ -18,43 +19,62 @@ module.exports = (function() {
// set pooling parameters if specified // set pooling parameters if specified
if (this.pooling) { if (this.pooling) {
this.pg.defaults.poolSize = this.config.poolCfg.maxConnections this.pg.defaults.poolSize = this.config.pool.maxConnections
this.pg.defaults.poolIdleTimeout = this.config.poolCfg.maxIdleTime this.pg.defaults.poolIdleTimeout = this.config.pool.maxIdleTime
} }
this.disconnectTimeoutId = null this.disconnectTimeoutId = null
this.pendingQueries = 0 this.pendingQueries = 0
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50) this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
process.on('exit', function() {
this.disconnect()
}.bind(this))
} }
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype) Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
var isConnecting = false ConnectorManager.prototype.endQuery = function() {
var isConnected = false
ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this var self = this
if (this.client === null) { if (!self.pooling && self.pendingQueries === 0) {
this.connect() setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self)
}, 100)
} }
var query = new Query(this.client, this.sequelize, callee, options || {})
self.pendingQueries += 1
return query.run(sql)
.success(function() { self.endQuery.call(self) })
.error(function() { self.endQuery.call(self) })
} }
ConnectorManager.prototype.endQuery = function() { ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this var self = this
self.pendingQueries -= 1
if (self.pendingQueries == 0) { // we really want pendingQueries to increment as fast as possible...
setTimeout(function() { self.pendingQueries++
self.pendingQueries == 0 && self.disconnect.call(self)
}, 100) return new Utils.CustomEventEmitter(function(emitter) {
} self.connect()
.on('error', function(err) {
// zero-out the previous increment
self.pendingQueries--
self.endQuery.call(self)
emitter.emit('error', err)
})
.on('success', function(done) {
var query = new Query(self.client, self.sequelize, callee, options || {})
done = done || null
query.run(sql, done)
.success(function(results) {
self.pendingQueries--
emitter.emit('success', results)
self.endQuery.call(self)
})
.error(function(err) {
self.pendingQueries--
emitter.emit('error', err)
self.endQuery.call(self)
})
.on('sql', function(sql) { emitter.emit('sql', sql) })
})
}).run()
} }
ConnectorManager.prototype.connect = function() { ConnectorManager.prototype.connect = function() {
...@@ -63,7 +83,8 @@ module.exports = (function() { ...@@ -63,7 +83,8 @@ module.exports = (function() {
// in case database is slow to connect, prevent orphaning the client // in case database is slow to connect, prevent orphaning the client
if (this.isConnecting) { if (this.isConnecting) {
return emitter.emit('success')
return emitter
} }
this.isConnecting = true this.isConnecting = true
...@@ -71,10 +92,14 @@ module.exports = (function() { ...@@ -71,10 +92,14 @@ module.exports = (function() {
var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config) var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config)
var connectCallback = function(err, client) { var connectCallback = function(err, client, done) {
self.isConnecting = false self.isConnecting = false
if (!!err) { if (!!err) {
// release the pool immediately, very important.
done && done(err)
if (err.code) {
switch(err.code) { switch(err.code) {
case 'ECONNREFUSED': case 'ECONNREFUSED':
emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings.")) emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
...@@ -86,33 +111,47 @@ module.exports = (function() { ...@@ -86,33 +111,47 @@ module.exports = (function() {
break break
default: default:
emitter.emit('error', err) emitter.emit('error', err)
break
}
} }
} else if (client) { } else if (client) {
client.query("SET TIME ZONE 'UTC'") client.query("SET TIME ZONE 'UTC'").on('end', function() {
.on('end', function() {
self.isConnected = true self.isConnected = true
this.client = client self.client = client
emitter.emit('success', done)
}); });
} else { } else {
this.client = null self.client = null
emitter.emit('success', done)
} }
} }
if (this.pooling) { if (this.pooling) {
// acquire client from pool // acquire client from pool
this.pg.connect(uri, connectCallback) this.poolIdentifier = this.pg.pools.getOrCreate(this.sequelize.config)
this.poolIdentifier.connect(connectCallback)
} else {
if (this.client !== null) {
connectCallback(null, this.client)
} else { } else {
//create one-off client //create one-off client
this.client = new this.pg.Client(uri) this.client = new this.pg.Client(uri)
this.client.connect(connectCallback) this.client.connect(connectCallback)
} }
}
return emitter return emitter
} }
ConnectorManager.prototype.disconnect = function() { ConnectorManager.prototype.disconnect = function() {
var self = this if (this.poolIdentifier) {
if (this.client) this.client.end() this.poolIdentifier.destroyAllNow()
}
if (this.client) {
this.client.end()
}
this.client = null this.client = null
this.isConnecting = false this.isConnecting = false
this.isConnected = false this.isConnected = false
......
...@@ -112,7 +112,7 @@ module.exports = (function() { ...@@ -112,7 +112,7 @@ module.exports = (function() {
}, },
describeTableQuery: function(tableName) { describeTableQuery: function(tableName) {
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS special FROM information_schema.columns c WHERE table_name = <%= table %>;' var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %>;'
return Utils._.template(query)({ return Utils._.template(query)({
table: this.escape(tableName) table: this.escape(tableName)
}) })
...@@ -189,6 +189,15 @@ module.exports = (function() { ...@@ -189,6 +189,15 @@ module.exports = (function() {
definition = definition.replace(/^ENUM\(.+\)/, this.quoteIdentifier("enum_" + tableName + "_" + attributeName)) definition = definition.replace(/^ENUM\(.+\)/, this.quoteIdentifier("enum_" + tableName + "_" + attributeName))
} }
if (definition.match(/UNIQUE;*$/)) {
definition = definition.replace(/UNIQUE;*$/, '')
attrSql += Utils._.template(query.replace('ALTER COLUMN', ''))({
tableName: this.quoteIdentifiers(tableName),
query: 'ADD CONSTRAINT ' + this.quoteIdentifier(attributeName + '_unique_idx') + ' UNIQUE (' + this.quoteIdentifier(attributeName) + ')'
})
}
attrSql += Utils._.template(query)({ attrSql += Utils._.template(query)({
tableName: this.quoteIdentifiers(tableName), tableName: this.quoteIdentifiers(tableName),
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition
......
...@@ -18,8 +18,9 @@ module.exports = (function() { ...@@ -18,8 +18,9 @@ module.exports = (function() {
} }
Utils.inherit(Query, AbstractQuery) Utils.inherit(Query, AbstractQuery)
Query.prototype.run = function(sql) { Query.prototype.run = function(sql, done) {
this.sql = sql this.sql = sql
var self = this
if (this.options.logging !== false) { if (this.options.logging !== false) {
this.options.logging('Executing: ' + this.sql) this.options.logging('Executing: ' + this.sql)
...@@ -39,13 +40,14 @@ module.exports = (function() { ...@@ -39,13 +40,14 @@ module.exports = (function() {
}.bind(this)) }.bind(this))
query.on('end', function() { query.on('end', function() {
done && done()
this.emit('sql', this.sql) this.emit('sql', this.sql)
if (receivedError) { if (receivedError) {
return return
} }
onSuccess.call(this, rows) onSuccess.call(this, rows, sql)
}.bind(this)) }.bind(this))
return this return this
...@@ -55,11 +57,11 @@ module.exports = (function() { ...@@ -55,11 +57,11 @@ module.exports = (function() {
return 'id' return 'id'
} }
var onSuccess = function(rows) { var onSuccess = function(rows, sql) {
var results = [] var results = []
, self = this , self = this
, isTableNameQuery = (this.sql.indexOf('SELECT table_name FROM information_schema.tables') === 0) , isTableNameQuery = (sql.indexOf('SELECT table_name FROM information_schema.tables') === 0)
, isRelNameQuery = (this.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0) , isRelNameQuery = (sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0)
if (isTableNameQuery || isRelNameQuery) { if (isTableNameQuery || isRelNameQuery) {
if (isRelNameQuery) { if (isRelNameQuery) {
......
...@@ -22,7 +22,7 @@ module.exports = (function() { ...@@ -22,7 +22,7 @@ module.exports = (function() {
this.database = db = new sqlite3.Database(self.sequelize.options.storage || ':memory:', function(err) { this.database = db = new sqlite3.Database(self.sequelize.options.storage || ':memory:', function(err) {
if (err) { if (err) {
if (err.code === "SQLITE_CANTOPEN") { if (err.code === "SQLITE_CANTOPEN") {
emitter.emit('error', new Error("Failed to find SQL server. Please double check your settings.")) emitter.emit('error', 'Failed to find SQL server. Please double check your settings.')
} }
} }
......
...@@ -37,9 +37,8 @@ module.exports = (function() { ...@@ -37,9 +37,8 @@ module.exports = (function() {
return Utils._.template(query)({}) return Utils._.template(query)({})
}, },
dropSchema: function() { dropSchema: function(tableName, options) {
var query = "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';" return this.dropTableQuery(tableName, options)
return Utils._.template(query)({})
}, },
showSchemasQuery: function() { showSchemasQuery: function() {
...@@ -89,6 +88,16 @@ module.exports = (function() { ...@@ -89,6 +88,16 @@ module.exports = (function() {
return this.replaceBooleanDefaults(sql) return this.replaceBooleanDefaults(sql)
}, },
dropTableQuery: function(tableName, options) {
options = options || {}
var query = "DROP TABLE IF EXISTS <%= table %>;"
return Utils._.template(query)({
table: this.quoteIdentifier(tableName)
})
},
addColumnQuery: function() { addColumnQuery: function() {
var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments) var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments)
return this.replaceBooleanDefaults(sql) return this.replaceBooleanDefaults(sql)
...@@ -134,6 +143,7 @@ module.exports = (function() { ...@@ -134,6 +143,7 @@ module.exports = (function() {
return Utils._.template(query)(replacements) return Utils._.template(query)(replacements)
}, },
selectQuery: function(tableName, options) { selectQuery: function(tableName, options) {
var table = null, var table = null,
joinQuery = "" joinQuery = ""
......
...@@ -54,6 +54,12 @@ module.exports = (function() { ...@@ -54,6 +54,12 @@ module.exports = (function() {
return this return this
} }
CustomEventEmitter.prototype.sql =
function(fct) {
this.on('sql', bindToProcess(fct))
return this;
}
CustomEventEmitter.prototype.proxy = function(emitter) { CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) { proxyEventKeys.forEach(function (eventKey) {
this.on(eventKey, function (result) { this.on(eventKey, function (result) {
......
const fs = require("fs") const fs = require("fs")
, moment = require("moment") , moment = require("moment")
var Utils = require("./utils") var Utils = require(__dirname + "/utils")
, Migration = require("./migration") , Migration = require(__dirname + "/migration")
, DataTypes = require("./data-types") , DataTypes = require(__dirname + "/data-types")
module.exports = (function() { module.exports = (function() {
var Migrator = function(sequelize, options) { var Migrator = function(sequelize, options) {
...@@ -45,7 +45,7 @@ module.exports = (function() { ...@@ -45,7 +45,7 @@ module.exports = (function() {
if (err) { if (err) {
emitter.emit('error', err) emitter.emit('error', err)
} else { } else {
var chainer = new Utils.QueryChainer var chainer = new Utils.QueryChainer()
, from = migrations[0] , from = migrations[0]
if (options.method === 'down') { if (options.method === 'down') {
...@@ -188,7 +188,7 @@ module.exports = (function() { ...@@ -188,7 +188,7 @@ 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()
var addMigration = function(filename) { var addMigration = function(filename) {
self.options.logging('Adding migration script at ' + filename) self.options.logging('Adding migration script at ' + filename)
var migration = new Migration(self, filename) var migration = new Migration(self, filename)
......
var Utils = require("./utils") var Utils = require(__dirname + "/utils")
module.exports = (function() { module.exports = (function() {
var QueryChainer = function(emitters) { var QueryChainer = function(emitters) {
......
var Utils = require('./utils') var Utils = require(__dirname + '/utils')
, DataTypes = require('./data-types') , DataTypes = require(__dirname + '/data-types')
, SQLiteQueryInterface = require('./dialects/sqlite/query-interface') , SQLiteQueryInterface = require(__dirname + '/dialects/sqlite/query-interface')
module.exports = (function() { module.exports = (function() {
var QueryInterface = function(sequelize) { var QueryInterface = function(sequelize) {
...@@ -54,7 +54,7 @@ module.exports = (function() { ...@@ -54,7 +54,7 @@ module.exports = (function() {
var showSchemasSql = self.QueryGenerator.showSchemasQuery() var showSchemasSql = self.QueryGenerator.showSchemasQuery()
self.sequelize.query(showSchemasSql, null, { raw: true }).success(function(schemaNames) { self.sequelize.query(showSchemasSql, null, { raw: true }).success(function(schemaNames) {
self.emit('showAllSchemas', null) self.emit('showAllSchemas', null)
emitter.emit('success', Utils._.flatten(Utils._.map(schemaNames, function(value){ return value.schema_name }))) emitter.emit('success', Utils._.flatten(Utils._.map(schemaNames, function(value){ return (!!value.schema_name ? value.schema_name : value) })))
}).error(function(err) { }).error(function(err) {
self.emit('showAllSchemas', err) self.emit('showAllSchemas', err)
emitter.emit('error', err) emitter.emit('error', err)
...@@ -266,23 +266,128 @@ module.exports = (function() { ...@@ -266,23 +266,128 @@ module.exports = (function() {
} }
QueryInterface.prototype.update = function(dao, tableName, values, identifier) { QueryInterface.prototype.update = function(dao, tableName, values, identifier) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier) var self = this
return queryAndEmit.call(this, [sql, dao], 'update') , restrict = false
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
// Check for a restrict field
if (!!dao.daoFactory && !!dao.daoFactory.associations) {
var keys = Object.keys(dao.daoFactory.associations)
, length = keys.length
for (var i = 0; i < length; i++) {
if (dao.daoFactory.associations[keys[i]].options && dao.daoFactory.associations[keys[i]].options.onUpdate && dao.daoFactory.associations[keys[i]].options.onUpdate === "restrict") {
restrict = true
}
}
}
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('success', results[1])
emitter.emit('sql', sql)
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('error', err)
emitter.emit('sql', sql)
})
.on('sql', function(sql) {
emitter.emit('sql', sql)
})
}).run()
} }
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier) { QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier) var self = this
return queryAndEmit.call(this, sql, 'bulkUpdate') , sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkUpdate'])
return chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
} }
QueryInterface.prototype.delete = function(dao, tableName, identifier) { QueryInterface.prototype.delete = function(dao, tableName, identifier) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier) var self = this
return queryAndEmit.call(this, [sql, dao], 'delete') , restrict = false
, sql = self.QueryGenerator.deleteQuery(tableName, identifier)
// Check for a restrict field
if (!!dao.daoFactory && !!dao.daoFactory.associations) {
var keys = Object.keys(dao.daoFactory.associations)
, length = keys.length
for (var i = 0; i < length; i++) {
if (dao.daoFactory.associations[keys[i]].options && dao.daoFactory.associations[keys[i]].options.onDelete && dao.daoFactory.associations[keys[i]].options.onDelete === "restrict") {
restrict = true
}
}
}
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
} }
QueryInterface.prototype.bulkDelete = function(tableName, identifier, options) { QueryInterface.prototype.bulkDelete = function(tableName, identifier, options) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null})) var self = this
return queryAndEmit.call(this, sql, 'bulkDelete') var sql = self.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null}))
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkDelete', options])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
} }
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) { QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
...@@ -385,9 +490,7 @@ module.exports = (function() { ...@@ -385,9 +490,7 @@ module.exports = (function() {
return this.QueryGenerator.escape(value) return this.QueryGenerator.escape(value)
} }
// private var queryAndEmit = QueryInterface.prototype.queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) {
var queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) {
options = Utils._.extend({ options = Utils._.extend({
success: function(){}, success: function(){},
error: function(){} error: function(){}
......
...@@ -88,9 +88,12 @@ var Utils = module.exports = { ...@@ -88,9 +88,12 @@ var Utils = module.exports = {
type = typeof where[i] type = typeof where[i]
_where[i] = _where[i] || {} _where[i] = _where[i] || {}
if (Array.isArray(where[i])) { if (where[i] === null) {
// skip nulls
}
else if (Array.isArray(where[i])) {
_where[i].in = _where[i].in || [] _where[i].in = _where[i].in || []
_where[i].in.concat(where[i]); _where[i].in.concat(where[i])
} }
else if (type === "object") { else if (type === "object") {
Object.keys(where[i]).forEach(function(ii) { Object.keys(where[i]).forEach(function(ii) {
...@@ -249,6 +252,23 @@ var Utils = module.exports = { ...@@ -249,6 +252,23 @@ var Utils = module.exports = {
isHash: function(obj) { isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj); return Utils._.isObject(obj) && !Array.isArray(obj);
}, },
hasChanged: function(attrValue, value) {
//If attribute value is Date, check value as a date
if (Utils._.isDate(attrValue) && !Utils._.isDate(value)) {
value = new Date(value)
}
if (Utils._.isDate(attrValue)) {
return attrValue.valueOf() !== value.valueOf()
}
//If both of them are empty, don't set as changed
if ((attrValue === undefined || attrValue === null || attrValue === '') && (value === undefined || value === null || value === '')) {
return false
}
return attrValue !== value
},
argsArePrimaryKeys: function(args, primaryKeys) { argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length == Object.keys(primaryKeys).length) var result = (args.length == Object.keys(primaryKeys).length)
if (result) { if (result) {
...@@ -380,6 +400,6 @@ var Utils = module.exports = { ...@@ -380,6 +400,6 @@ var Utils = module.exports = {
} }
} }
Utils.CustomEventEmitter = require("./emitters/custom-event-emitter") Utils.CustomEventEmitter = require(__dirname + "/emitters/custom-event-emitter")
Utils.QueryChainer = require("./query-chainer") Utils.QueryChainer = require(__dirname + "/query-chainer")
Utils.Lingo = require("lingo") Utils.Lingo = require("lingo")
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
"janzeh@gmail.com", "janzeh@gmail.com",
"jmei@itu.dk" "jmei@itu.dk"
] ]
},
{
"name": "Daniel Durante",
"email": "me@danieldurante.com"
} }
], ],
"repository": { "repository": {
...@@ -37,21 +41,24 @@ ...@@ -37,21 +41,24 @@
"lingo": "~0.0.5", "lingo": "~0.0.5",
"validator": "~1.3.0", "validator": "~1.3.0",
"moment": "~2.1.0", "moment": "~2.1.0",
"commander": "~1.3.0", "commander": "~2.0.0",
"dottie": "0.0.8-0", "dottie": "0.0.8-0",
"toposort-class": "~0.2.0", "toposort-class": "~0.2.0",
"generic-pool": "2.0.3", "generic-pool": "2.0.4",
"promise": "~3.2.0", "promise": "~3.2.0",
"sql": "~0.26.0" "sql": "~0.26.0"
}, },
"devDependencies": { "devDependencies": {
"sqlite3": "~2.1.12", "sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha8", "mysql": "~2.0.0-alpha8",
"pg": "~2.1.0", "pg": "~2.2.0",
"buster": "~0.6.3", "buster": "~0.6.3",
"watchr": "~2.4.3", "watchr": "~2.4.3",
"yuidocjs": "~0.3.36", "yuidocjs": "~0.3.36",
"semver": "~2.0.8" "chai": "~1.7.2",
"mocha": "~1.12.0",
"chai-datetime": "~1.0.0",
"sinon": "~1.7.3"
}, },
"keywords": [ "keywords": [
"mysql", "mysql",
...@@ -61,13 +68,7 @@ ...@@ -61,13 +68,7 @@
], ],
"main": "index", "main": "index",
"scripts": { "scripts": {
"test": "npm run test-buster", "test": "make all",
"test-buster": "npm run test-buster-mysql && npm run test-buster-postgres && npm run test-buster-postgres-native && npm run test-buster-sqlite",
"test-buster-travis": "node_modules/.bin/buster-test",
"test-buster-mysql": "DIALECT=mysql node_modules/.bin/buster-test",
"test-buster-postgres": "DIALECT=postgres node_modules/.bin/buster-test",
"test-buster-postgres-native": "DIALECT=postgres-native node_modules/.bin/buster-test",
"test-buster-sqlite": "DIALECT=sqlite node_modules/.bin/buster-test",
"docs": "node_modules/.bin/yuidoc . -o docs" "docs": "node_modules/.bin/yuidoc . -o docs"
}, },
"bin": { "bin": {
......
/* jshint camelcase: false */
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('general usage', function() {
before(function(done) {
this.User = this.sequelize.define('User', {
username: Helpers.Sequelize.STRING,
enabled: {
type: Helpers.Sequelize.BOOLEAN,
defaultValue: true
}
})
this.Task = this.sequelize.define('Task', {
title: Helpers.Sequelize.STRING
})
this.sequelize.sync({ force: true }).success(done)
})
it('adds the foreign key', function(done) {
this.Task.belongsTo(this.User)
expect(this.Task.attributes.UserId).toEqual("INTEGER")
done()
})
it("underscores the foreign key", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {underscored: true})
Task.belongsTo(this.User)
expect(Task.attributes.user_id).toEqual("INTEGER")
done()
})
it("uses the passed foreign key", function(done) {
this.Task.belongsTo(this.User, {foreignKey: 'person_id'})
expect(this.Task.attributes.person_id).toEqual("INTEGER")
done()
})
it("defines getters and setters", function(done) {
this.Task.belongsTo(this.User)
var task = this.Task.build({title: 'asd'})
expect(task.setUser).toBeDefined()
expect(task.getUser).toBeDefined()
done()
})
it("aliases the getters and setters according to the passed 'as' option", function(done) {
this.Task.belongsTo(this.User, {as: 'Person'})
var task = this.Task.build({title: 'asd'})
expect(task.setPerson).toBeDefined()
expect(task.getPerson).toBeDefined()
done()
})
it("aliases associations to the same table according to the passed 'as' option", function(done) {
this.Task.belongsTo(this.User, {as: 'Poster'})
this.Task.belongsTo(this.User, {as: 'Owner'})
var task = this.Task.build({title: 'asd'})
expect(task.getPoster).toBeDefined()
expect(task.setPoster).toBeDefined()
expect(task.getOwner).toBeDefined()
expect(task.setOwner).toBeDefined()
done()
})
it("intializes the foreign key with null", function(done) {
this.Task.belongsTo(this.User)
var task = this.Task.build({title: 'asd'})
expect(task.UserId).not.toBeDefined();
done()
})
it("sets and gets the correct objects", function(done) {
var self = this
this.Task.belongsTo(this.User, {as: 'User'})
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'asd'}).success(function(u) {
self.Task.create({title: 'a task'}).success(function(t) {
t.setUser(u).success(function() {
t.getUser().success(function(user) {
expect(user.username).toEqual('asd')
done()
})
})
})
})
})
})
it('extends the id where param with the supplied where params', function(done) {
var self = this
this.Task.belongsTo(this.User, {as: 'User'})
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'asd', enabled: false}).success(function(u) {
self.Task.create({title: 'a task'}).success(function(t) {
t.setUser(u).success(function() {
t.getUser({where: {enabled: true}}).success(function(user) {
expect(user).toEqual(null)
done()
})
})
})
})
})
})
it("handles self associations", function(done) {
var Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.belongsTo(Person, {as: 'Mother', foreignKey: 'MotherId'})
Person.belongsTo(Person, {as: 'Father', foreignKey: 'FatherId'})
Person.sync({force: true}).success(function() {
var p = Person.build()
expect(p.setFather).toBeDefined()
expect(p.setMother).toBeDefined()
done()
})
})
it("sets the foreign key in self associations", function(done) {
var Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.belongsTo(Person, {as: 'Mother'})
expect(Person.associations.MotherPersons.options.foreignKey).toEqual('MotherId')
done()
})
})
describe('setAssociation', function() {
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).not.toEqual(null)
task.setUserXYZ(null).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).toEqual(null)
done()
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User)
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.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onDelete: 'cascade'})
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.findAll().success(function(tasks) {
expect(tasks.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onDelete: 'restrict'})
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().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onUpdate: 'cascade'})
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() {
// 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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onUpdate: 'restrict'})
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() {
// 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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
Task.belongsTo(User, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
/* jshint camelcase: false */
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
, moment = require('moment')
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe(Helpers.getTestDialectTeaser("HasMany"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
})
describe('general usage', function() {
before(function(done) {
this.User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING })
this.sequelize.sync({ force: true }).success(done)
})
describe('mono-directional', function() {
it("adds the foreign key", function(done) {
this.User.hasMany(this.Task)
expect(this.Task.attributes.UserId).toEqual("INTEGER")
done()
})
it('adds the foreign key with underscore', function(done) {
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING }, { underscored: true })
Task.hasMany(User)
expect(User.attributes.task_id).toBeDefined()
done()
})
it('uses the passed foreign key', function(done) {
this.User.hasMany(this.Task, { foreignKey: 'person_id' })
expect(this.Task.attributes.person_id).toEqual("INTEGER")
done()
})
it('defines getters and setters', function(done) {
this.User.hasMany(this.Task)
var u = this.User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
done()
})
it("defines getters and setters according to the 'as' option", function(done) {
this.User.hasMany(this.Task, {as: 'Tasks'})
var u = this.User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
done()
})
it("sets and gets associated objects", function(done) {
var self = this
this.User.hasMany(this.Task, { as: 'Tasks' })
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'name'}).success(function(user) {
self.Task.create({title: 'task1'}).success(function(task1) {
self.Task.create({title: 'task2'}).success(function(task2) {
user.setTasks([task1, task2]).success(function() {
user.getTasks().success(function(tasks) {
expect(tasks.length).toEqual(2)
user.getTasks({attributes: ['title']}).success(function(tasks) {
expect(tasks[0].selectedValues.title).toEqual('task1')
expect(tasks[0].selectedValues.id).not.toBeDefined()
done()
})
})
})
})
})
})
})
})
})
it("should allow selfAssociation to be single linked (only one DAO is created)", function(done) {
var oldLength = this.sequelize.daoFactoryManager.daos.length
, Comment = this.sequelize.define('Comment', { content: Helpers.Sequelize.STRING })
Comment.belongsTo(Comment, {as: "Parent"});
Comment.hasMany(Comment, {as: 'Children', foreignKey: "ParentId", useJunctionTable: false})
expect(this.sequelize.daoFactoryManager.daos.length).toEqual(oldLength + 1)
Comment.sync({force: true}).success(function() {
Comment.create({ content: 'parentComment' }).success(function(parent) {
Comment.create({ content: 'child1' }).success(function(child1) {
child1.setParent(parent).success(function() {
Comment.create({ content: 'child2' }).success(function(child2) {
child2.setParent(parent).success(function() {
Comment.find({where: { content: 'parentComment' }}).success(function(parent) {
parent.getChildren().success(function(children) {
expect(children.length).toEqual(2)
done()
})
})
})
})
})
})
})
})
})
it("should still use many to many for selfAssociation by default (two DAOs are created)", function(done) {
var oldLength = this.sequelize.daoFactoryManager.daos.length
, Comment = this.sequelize.define('Comment', { content: Sequelize.STRING })
Comment.belongsTo(Comment, {as: "Parent"})
Comment.hasMany(Comment, {as: 'Children'})
expect(this.sequelize.daoFactoryManager.daos.length).toEqual(oldLength + 2)
done();
})
describe('bi-directional', function() {
it('adds the foreign key', function(done) {
var self = this
this.Task.hasMany(this.User)
this.User.hasMany(this.Task)
expect(this.Task.attributes.UserId).not.toBeDefined()
expect(this.User.attributes.UserId).not.toBeDefined()
var daos = this.sequelize.daoFactoryManager.daos.filter(function(dao) {
return (dao.tableName == (self.Task.tableName + self.User.tableName))
})
daos.forEach(function(dao) {
expect(dao.attributes.UserId).toBeDefined()
expect(dao.attributes.TaskId).toBeDefined()
})
done()
})
it("adds the foreign key with underscores", function(done) {
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING }, { underscored: true })
, Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING })
Task.hasMany(User)
User.hasMany(Task)
expect(Task.attributes.user_id).not.toBeDefined()
expect(User.attributes.user_id).not.toBeDefined()
var daos = this.sequelize.daoFactoryManager.daos.filter(function(dao) {
return (dao.tableName == (Task.tableName + User.tableName))
})
daos.forEach(function(dao) {
expect(dao.attributes.user_id).toBeDefined()
expect(dao.attributes.TaskId).toBeDefined()
})
done()
})
it("uses the passed foreign keys", function(done) {
var self = this
this.User.hasMany(this.Task, { foreignKey: 'person_id' })
this.Task.hasMany(this.User, { foreignKey: 'work_item_id' })
var daos = this.sequelize.daoFactoryManager.daos.filter(function(dao) {
return (dao.tableName == (self.Task.tableName + self.User.tableName))
})
daos.forEach(function(dao) {
expect(dao.attributes.person_id).toBeDefined()
expect(dao.attributes.work_item_id).toBeDefined()
})
done()
})
it("defines getters and setters", function(done) {
this.User.hasMany(this.Task)
this.Task.hasMany(this.User)
var u = this.User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = this.Task.build({ title: 'foobar' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
done()
})
it("defines getters and setters according to the 'as' option", function(done) {
this.User.hasMany(this.Task, { as: 'Tasks' })
this.Task.hasMany(this.User, { as: 'Users' })
var u = this.User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = this.Task.build({ title: 'asd' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
done()
})
it("sets and gets the corrected associated objects", function(done) {
var self = this
var users = []
, tasks = []
this.User.hasMany(this.Task, {as: 'Tasks'})
this.Task.hasMany(this.User, {as: 'Users'})
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'name'}).success(function(user1) {
self.User.create({username: 'name2'}).success(function(user2) {
self.Task.create({title: 'task1'}).success(function(task1) {
self.Task.create({title: 'task2'}).success(function(task2) {
users.push(user1)
users.push(user2)
tasks.push(task1)
tasks.push(task2)
users[0].setTasks(tasks).success(function() {
users[0].getTasks().success(function(_tasks) {
expect(_tasks.length).toEqual(2)
tasks[1].setUsers(users).success(function() {
tasks[1].getUsers().success(function(_users) {
expect(users.length).toEqual(2)
done()
})
})
})
})
})
})
})
})
})
})
})
it("build the connector daos name", function(done) {
var self = this
, Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.hasMany(Person, {as: 'Children'})
Person.hasMany(Person, {as: 'Friends'})
Person.hasMany(Person, {as: 'CoWorkers'})
Person.sync({force: true}).success(function() {
var daoNames = self.sequelize.daoFactoryManager.daos.map(function(dao) { return dao.tableName })
, expectation = ["Persons", "ChildrenPersons", "CoWorkersPersons", "FriendsPersons"]
expectation.forEach(function(ex) {
expect(daoNames.indexOf(ex) > -1).toBeTruthy()
})
done()
})
})
it("allows join table to be specified", function(done) {
var Child = this.sequelize.define('Child', { name: Helpers.Sequelize.STRING }, {underscore: true, freezeTableName: true})
, Parent = this.sequelize.define('Parent', { name: Helpers.Sequelize.STRING }, {underscore: true, freezeTableName: true})
, ParentJoin = this.sequelize.define('ParentRelationship', { parent_id: Helpers.Sequelize.INTEGER, child_id: Helpers.Sequelize.INTEGER }, {underscore: true, freezeTableName: true})
, parents = []
Parent.hasMany(Child, {as: 'Children', foreignKey: 'child_id', joinTableName: 'ParentRelationship'})
Child.hasMany(Parent, {as: 'Parents', foreignKey: 'parent_id', joinTableName: 'ParentRelationship'})
this.sequelize.sync({ force: true }).success(function() {
Parent.create({name: 'mom'}).success(function(mom) {
parents.push(mom)
Parent.create({name: 'dad'}).success(function(dad) {
parents.push(dad)
Child.create({name: 'baby'}).success(function(baby) {
baby.setParents(parents).success(function(){
parents[0].getChildren().success(function(children){
expect(children).not.toBe(null)
expect(children.length).toBeDefined()
expect(children.length).toEqual(1)
expect(children[0]).toBeDefined()
expect(children[0].name).toEqual('baby')
done()
})
})
})
})
})
})
})
it("allows join table to be mapped and specified", function(done) {
var User = this.sequelize.define('User', { name: Helpers.Sequelize.STRING }, {underscore: true, freezeTableName: true})
, Company = this.sequelize.define('Company', { name: Helpers.Sequelize.STRING }, {underscore: true, freezeTableName: true})
, CompanyAccess = this.sequelize.define('CompanyAccess', { company_id: Helpers.Sequelize.INTEGER, user_id: Helpers.Sequelize.INTEGER, permission: Helpers.Sequelize.STRING }, {underscore: true, freezeTableName: true})
, companies = []
CompanyAccess.belongsTo(User, {as: 'User', foreignKey: 'user_id'})
CompanyAccess.belongsTo(Company, {as: 'Company', foreignKey: 'company_id'})
User.hasMany(Company, {as: 'Companies', foreignKey: 'user_id', joinTableName: 'CompanyAccess'})
Company.hasMany(User, {as: 'Users', foreignKey: 'company_id', joinTableName: 'CompanyAccess'})
this.sequelize.sync({ force: true }).success(function() {
Company.create({name: 'IBM'}).success(function(ibm) {
companies.push(ibm)
Company.create({name: 'EA'}).success(function(ea) {
companies.push(ea)
User.create({name: 'joe@ibm.com'}).success(function(joe) {
joe.setCompanies(companies).success(function(){
User.find({where: {name: 'joe@ibm.com'}}).success(function(joe) {
expect(joe).not.toEqual(null)
joe.getCompanies().success(function(comps) {
expect(comps).not.toEqual(null)
expect(comps.length).toEqual(2)
done()
})
})
})
})
})
})
})
})
it("gets and sets the connector daos", function(done) {
var Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.hasMany(Person, {as: 'Children'})
Person.hasMany(Person, {as: 'Friends'})
Person.hasMany(Person, {as: 'CoWorkers'})
Person.sync({ force: true }).success(function() {
Person.create({name: 'foobar'}).success(function(person) {
Person.create({name: 'friend'}).success(function(friend) {
person.setFriends([friend]).success(function() {
person.getFriends().success(function(friends) {
expect(friends.length).toEqual(1)
expect(friends[0].name).toEqual('friend')
done()
})
})
})
})
})
})
})
describe('(1:N)', function() {
describe('hasSingle', function() {
before(function(done) {
this.Article = this.sequelize.define('Article', {
'title': Sequelize.STRING
})
this.Label = this.sequelize.define('Label', {
'text': Sequelize.STRING
})
this.Article.hasMany(this.Label)
this.sequelize.sync({ force: true }).success(done)
})
it('does not have any labels assigned to it initially', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
var chainer = new Sequelize.Utils.QueryChainer([
article.hasLabel(label1),
article.hasLabel(label2)
])
chainer.run().success(function(_, hasLabel1, hasLabel2) {
expect(hasLabel1).toBeFalse()
expect(hasLabel2).toBeFalse()
done()
})
})
})
it('answers true if the label has been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
var chainer = new Sequelize.Utils.QueryChainer([
[ article, 'addLabel', [ label1 ]],
[ article, 'hasLabel', [ label1 ]],
[ article, 'hasLabel', [ label2 ]]
])
chainer.runSerially()
.success(function(_, label1, hasLabel1, hasLabel2) {
expect(hasLabel1).toBeTrue()
expect(hasLabel2).toBeFalse()
done()
})
})
})
})
describe('hasAll', function() {
before(function(done) {
this.Article = this.sequelize.define('Article', {
'title': Sequelize.STRING
})
this.Label = this.sequelize.define('Label', {
'text': Sequelize.STRING
})
this.Article.hasMany(this.Label)
this.sequelize.sync({ force: true }).success(done)
})
it('answers false if only some labels have been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
article.addLabel(label1).success(function() {
article.hasLabels([label1, label2]).success(function(result) {
expect(result).toBeFalse()
done()
})
})
})
})
it('answers true if all label have been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
article.setLabels([label1, label2]).success(function() {
article.hasLabels([label1, label2]).success(function(result) {
expect(result).toBeTrue()
done()
})
})
})
})
})
describe('setAssociations', function() {
it("clears associations when passing null to the set-method", function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
})
it("clears associations when passing null to the set-method with omitNull set to true", function(done) {
this.sequelize.options.omitNull = true
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
describe("getting assocations with options", function() {
before(function(done) {
var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
this.User.hasMany(self.Task)
this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false})
])
chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(done)
})
})
})
it('should treat the where object of associations as a first class citizen', function(done) {
var self = this
this.Article = this.sequelize.define('Article', {
'title': Sequelize.STRING
})
this.Label = this.sequelize.define('Label', {
'text': Sequelize.STRING,
'until': Sequelize.DATE
})
this.Article.hasMany(this.Label)
this.sequelize.sync({ force: true }).success(function() {
var chainer = new Sequelize.Utils.QueryChainer([
self.Article.create({ title: 'Article' }),
self.Label.create({ text: 'Awesomeness', until: '2014-01-01 01:00:00' }),
self.Label.create({ text: 'Epicness', until: '2014-01-03 01:00:00' })
])
chainer.run().success(function(results, article, label1, label2) {
article.setLabels([label1, label2]).success(function() {
article.getLabels({where: ['until > ?', moment('2014-01-02').toDate()]}).success(function(labels) {
expect(labels).toBeArray()
expect(labels.length).toEqual(1)
expect(labels[0].text).toEqual('Epicness')
done()
})
})
})
})
})
it("gets all associated objects when no options are passed", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2)
done()
})
})
})
it("only get objects that fulfill the options", function(done) {
this.User.find({ where: { username: 'John' } }).success(function (john) {
john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' }).success(function (tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.sequelize.sync({force: true}).success(done)
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECt, only UPDAET
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECT, only UPDATE
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
})
describe('(N:M)', function() {
describe("getting assocations with options", function() {
before(function(done) {
var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
self.User.hasMany(self.Task)
self.Task.hasMany(self.User)
this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false})
])
chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(done)
})
})
})
it("gets all associated objects when no options are passed", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2)
done()
})
})
})
it("only get objects that fulfill the options", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: {active: true}}).success(function (tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
it("only gets objects that fulfill options with a formatted value", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: ['active = ?', true]}).success(function (tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
it("removes the reference id, which was added in the first place", function() {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
expect(Task.attributes.UserId).toBeDefined()
Task.hasMany(User)
expect(Task.attributes.UserId).not.toBeDefined()
})
it("adds three items to the query chainer when calling sync", function() {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
Task.hasMany(User)
var add = this.spy()
this.stub(Sequelize.Utils, 'QueryChainer').returns({ add: add, runSerially: function(){} })
this.sequelize.sync({ force: true })
expect(add).toHaveBeenCalledThrice()
})
describe('setAssociations', function() {
it("clears associations when passing null to the set-method", function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
it('uses one insert into statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("INSERT INTO")
expect(sql).toMatch("VALUES (1,1),(2,1)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one delete from statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("DELETE FROM")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for DELETE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
describe('join table creation', function () {
before(function (done) {
this.User = this.sequelize.define('User',
{ username: Sequelize.STRING },
{ tableName: 'users'}
)
this.Task = this.sequelize.define('Task',
{ title: Sequelize.STRING },
{ tableName: 'tasks' }
)
this.User.hasMany(this.Task,
{ joinTableName: 'user_has_tasks' }
)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
it('uses the specified joinTableName or a reasonable default', function(done) {
for (var associationName in this.User.associations) {
expect(associationName).not.toEqual(this.User.tableName)
expect(associationName).not.toEqual(this.Task.tableName)
var joinTableName = this.User.associations[associationName].options.joinTableName
if (typeof joinTableName !== 'undefined') {
expect(joinTableName).toEqual(associationName)
}
var tableName = this.User.associations[associationName].options.tableName
if (typeof tableName !== 'undefined') {
expect(tableName).toEqual(associationName)
}
}
done()
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task)
this.sequelize.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.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onDelete: 'cascade'})
this.sequelize.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.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onDelete: 'restrict'})
this.sequelize.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.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onUpdate: 'cascade'})
this.sequelize.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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onUpdate: 'restrict'})
this.sequelize.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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasMany(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
/* jshint camelcase: false */
if (typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../../index")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1500
describe(Helpers.getTestDialectTeaser("HasOne"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
})
describe('general usage', function() {
before(function(done) {
this.User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING })
this.sequelize.sync({ force: true }).success(done)
})
it("adds the foreign key", function(done) {
this.User.hasOne(this.Task)
expect(this.Task.attributes.UserId).toEqual("INTEGER")
done()
})
it("adds an underscored foreign key", function(done) {
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING }, {underscored: true})
, Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING })
User.hasOne(Task)
expect(Task.attributes.user_id).toEqual("INTEGER")
done()
})
it("uses the passed foreign key", function(done) {
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING }, {underscored: true})
, Task = this.sequelize.define('Task', { title: Helpers.Sequelize.STRING })
User.hasOne(Task, {foreignKey: 'person_id'})
expect(Task.attributes.person_id).toEqual("INTEGER")
done()
})
it("defines the getter and the setter", function(done) {
this.User.hasOne(this.Task)
var u = this.User.build({username: 'asd'})
expect(u.setTask).toBeDefined()
expect(u.getTask).toBeDefined()
done()
})
it("defined the getter and the setter according to the passed 'as' option", function(done) {
this.User.hasOne(this.Task, {as: 'Work'})
var u = this.User.build({username: 'asd'})
expect(u.setWork).toBeDefined()
expect(u.getWork).toBeDefined()
done()
})
it("aliases associations to the same table according to the passed 'as' option", function(done) {
this.User.hasOne(this.Task, {as: 'Work'});
this.User.hasOne(this.Task, {as: 'Play'});
var u = this.User.build({username: 'asd'})
expect(u.getWork).toBeDefined()
expect(u.setWork).toBeDefined()
expect(u.getPlay).toBeDefined()
expect(u.setPlay).toBeDefined()
done()
})
it("gets and sets the correct objects", function(done) {
var self = this
this.User.hasOne(this.Task, {as: 'Task'})
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'name'}).success(function(user) {
self.Task.create({title: 'snafu'}).success(function(task) {
user.setTask(task).on('success', function() {
user.getTask().on('success', function(task2) {
expect(task.title).toEqual(task2.title)
user.getTask({attributes: ['title']}).on('success', function(task2) {
expect(task2.selectedValues.title).toEqual('snafu')
expect(task2.selectedValues.id).not.toBeDefined()
done()
})
})
})
})
})
})
})
it("unsets unassociated objects", function(done) {
var self = this
this.User.hasOne(this.Task, {as: 'Task'})
this.sequelize.sync({ force: true }).success(function() {
self.User.create({username: 'name'}).success(function(user) {
self.Task.create({title: 'snafu'}).success(function(task1) {
self.Task.create({title: 'another task'}).success(function(task2) {
user.setTask(task1).success(function() {
user.getTask().success(function(_task) {
expect(task1.title).toEqual(_task.title)
user.setTask(task2).success(function() {
user.getTask().success(function(_task2) {
expect(task2.title).toEqual(_task2.title)
done()
})
})
})
})
})
})
})
})
})
it("sets self associations", function(done) {
var Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.hasOne(Person, {as: 'Mother', foreignKey: 'MotherId'})
Person.hasOne(Person, {as: 'Father', foreignKey: 'FatherId'})
Person.sync({force: true}).success(function() {
var p = Person.build()
expect(p.setFather).toBeDefined()
expect(p.setMother).toBeDefined()
done()
})
})
it("automatically sets the foreign key on self associations", function(done) {
var Person = this.sequelize.define('Person', { name: Helpers.Sequelize.STRING })
Person.hasOne(Person, {as: 'Mother'})
expect(Person.associations.MotherPersons.options.foreignKey).toEqual('MotherId')
done()
})
})
describe('getAssocation', function() {
it('should be able to handle a where object that\'s a first class citizen.', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING, status: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task', status: 'inactive' }).success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) {
expect(task).toEqual(null)
done()
})
})
})
})
})
})
})
describe('setAssociation', function() {
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).not.toEqual(null)
user.setTaskXYZ(null).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).toEqual(null)
done()
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.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.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'cascade'})
this.sequelize.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.findAll().success(function(tasks) {
expect(tasks.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'restrict'})
this.sequelize.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().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(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.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasOne(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Mixin"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('Mixin', function() {
var DAOFactory = require("../../lib/dao-factory")
it("adds the mixed-in functions to the dao", function() {
expect(DAOFactory.prototype.hasOne).toBeDefined()
expect(DAOFactory.prototype.hasMany).toBeDefined()
expect(DAOFactory.prototype.belongsTo).toBeDefined()
})
})
describe('getAssociation', function() {
it('returns the respective part of the association for 1:1 associations', function() {
var User = this.sequelize.define('User', {})
var Task = this.sequelize.define('Task', {})
User.hasOne(Task)
Task.belongsTo(User)
expect(User.getAssociation(Task).target).toEqual(Task)
})
it('can handle multiple associations just fine', function(done) {
var Emit = require('events').EventEmitter
, emit = new Emit()
, User = this.sequelize.define('User', { username: Sequelize.STRING })
, Event = this.sequelize.define('Event', { name: Sequelize.STRING })
, Place = this.sequelize.define('Place', { name: Sequelize.STRING })
, theSQL = ''
, count = 0
, theEvent
Event.belongsTo(User)
Event.hasOne(Place)
emit.on('sql', function(sql){
++count
theSQL = sql
if (count > 1) {
emit.emit('spy')
}
})
emit.on('find', function(e) {
++count
theEvent = e
if (count > 1) {
emit.emit('spy')
}
})
emit.on('spy', function(){
expect(theSQL.match(/WHERE ["`]Events["`].["`]id["`]=1;$/)).not.toBeNull()
expect(theEvent.name).toEqual('Bob Marley Concert')
expect(theEvent.place.name).toEqual('Backyard')
expect(theEvent.user.username).toEqual('Dan')
done()
})
this.sequelize.sync({force: true }).success(function() {
User.create({username: 'Dan'}).success(function(user){
Event.create({name: 'Bob Marley Concert'}).success(function(event) {
Place.create({name: 'Backyard'}).success(function(place) {
event.setUser(user).success(function(){
event.setPlace(place).success(function(){
Event.find({where: {id: 1}, include: [User, Place]}).success(function(theEvent){
emit.emit('find', theEvent)
})
.on('sql', function(sql){
emit.emit('sql', sql)
})
})
})
})
})
})
})
})
})
})
var config = module.exports
config["node tests"] = {
environment: "node",
rootPath: "../",
tests: [
"spec/**/*.spec.js"
]
}
var Factories = module.exports = function(helpers) {
this.helpers = helpers
this.sequelize = this.helpers.sequelize
}
Factories.prototype.DAO = function(daoName, options, callback, count) {
count = count || 1
var self = this
, daos = []
this.helpers.async(function(done) {
var DAO = self.sequelize.daoFactoryManager.getDAO(daoName)
var create = function(cb) {
DAO.create(options).on('success', function(dao) {
daos.push(dao)
cb && cb()
}).on('error', function(err) {
console.log(err)
done()
})
}
var cb = function() {
if(--count) {
create(cb)
} else {
done()
callback && callback(daos)
}
}
create(cb)
})
}
Factories.prototype.User = function(options, callback, count) {
this.DAO('User', options, callback, count)
}
Sequelize = require("../../index")
var Helpers = module.exports = function(sequelize) {
this.sequelize = sequelize
this.Factories = new (require("./factories"))(this)
}
Helpers.prototype.sync = function() {
var self = this
this.async(function(done) {
self.sequelize
.sync({force: true})
.success(done)
.failure(function(err) { console.log(err) })
})
}
Helpers.prototype.drop = function() {
var self = this
this.async(function(done) {
self.sequelize
.drop()
.on('success', done)
.on('error', function(err) { console.log(err) })
})
}
Helpers.prototype.dropAllTables = function() {
var self = this
this.async(function(done) {
self.sequelize
.getQueryInterface()
.dropAllTables()
.success(done)
.error(function(err) { console.log(err) })
})
}
Helpers.prototype.async = function(fct) {
var done = false
runs(function() {
fct(function() { return done = true })
})
waitsFor(function(){ return done })
}
if(typeof require === 'function') {
const buster = require("buster")
, semver = require("semver")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index')
, noDomains = semver.lt(process.version, '0.8.0')
describe(Helpers.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it('when we don\'t have the correct server details', function(done) {
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
expect('').toEqual('') // Silence Buster!
return done()
}
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to find MySQL server. Please double check your settings.'
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('when we don\'t have the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") {
console.log('This dialect doesn\'t support me :(')
expect('').toEqual('') // Silence Buster
return done()
}
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
expect('').toEqual('') // Silence Buster!
return done()
}
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to authenticate for MySQL. Please double check your settings.'
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('when we don\'t have a valid dialect.', function() {
Helpers.assertException(function() {
new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {host: '0.0.0.1', port: config[dialect].port, dialect: undefined})
}.bind(this), 'The dialect undefined is not supported.')
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
var config = sequelize.config
var options = sequelize.options
expect(options.dialect).toEqual('mysql')
expect(config.database).toEqual('dbname')
expect(config.host).toEqual('example.com')
expect(config.username).toEqual('user')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(9821)
})
it('should work with no authentication options', function() {
var sequelize = new Sequelize('mysql://example.com:9821/dbname')
var config = sequelize.config
expect(config.username).toEqual(undefined)
expect(config.password).toEqual(null)
})
it('should use the default port when no other is specified', function() {
var sequelize = new Sequelize('mysql://example.com/dbname')
var config = sequelize.config
// The default port should be set
expect(config.port).toEqual(3306)
})
})
describe('Intantiation with arguments', function() {
it('should accept two parameters (database, username)', function() {
var sequelize = new Sequelize('dbname', 'root')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
})
it('should accept three parameters (database, username, password)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
})
it('should accept four parameters (database, username, password, options)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(999)
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, _ = require('lodash')
, moment = require('moment')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
this.DataTypes = DataTypes
this.sequelize = sequelize
this.User = sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
})
}.bind(this),
onComplete: function() {
this.User.sync({ force: true }).success(done)
}.bind(this)
})
})
describe('constructor', function() {
it("uses the passed dao name as tablename if freezeTableName", function() {
var User = this.sequelize.define('FrozenUser', {}, { freezeTableName: true })
expect(User.tableName).toEqual('FrozenUser')
})
it("uses the pluralized dao name as tablename unless freezeTableName", function() {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
expect(User.tableName).toEqual('SuperUsers')
})
it("uses checks to make sure dao factory isnt leaking on multiple define", function() {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize = this.sequelize.daoFactoryManager.all.length
var User2 = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize2 = this.sequelize.daoFactoryManager.all.length
expect(factorySize).toEqual(factorySize2)
})
it("attaches class and instance methods", function() {
var User = this.sequelize.define('UserWithClassAndInstanceMethods', {}, {
classMethods: { doSmth: function(){ return 1 } },
instanceMethods: { makeItSo: function(){ return 2}}
})
expect(User.doSmth).toBeDefined()
expect(User.doSmth()).toEqual(1)
expect(User.makeItSo).not.toBeDefined()
expect(User.build().doSmth).not.toBeDefined()
expect(User.build().makeItSo).toBeDefined()
expect(User.build().makeItSo()).toEqual(2)
})
it("throws an error if 2 autoIncrements are passed", function() {
Helpers.assertException(function() {
this.sequelize.define('UserWithTwoAutoIncrements', {
userid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
userscore: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }
})
}.bind(this), 'Invalid DAO definition. Only one autoincrement field allowed.')
})
it('throws an error if a custom model-wide validation is not a function', function() {
Helpers.assertException(function() {
this.sequelize.define('Foo', {
field: {
type: Sequelize.INTEGER
}
}, {
validate: {
notFunction: 33
}
})
}.bind(this), 'Members of the validate option must be functions. Model: Foo, error with validate member notFunction')
})
it('throws an error if a custom model-wide validation has the same name as a field', function() {
Helpers.assertException(function() {
this.sequelize.define('Foo', {
field: {
type: Sequelize.INTEGER
}
}, {
validate: {
field: function() {}
}
})
}.bind(this), 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field')
})
})
describe('build', function() {
it("doesn't create database entries", function(done) {
this.User.build({ username: 'John Wayne' })
this.User.all().success(function(users) {
expect(users.length).toEqual(0)
done()
})
})
it("fills the objects with default values", function() {
var Task = this.sequelize.define('Task', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
})
expect(Task.build().title).toEqual('a task!')
expect(Task.build().foo).toEqual(2)
expect(Task.build().bar).toEqual(undefined)
expect(Task.build().foobar).toEqual('asd')
expect(Task.build().flag).toEqual(false)
})
it("fills the objects with default values", function() {
var Task = this.sequelize.define('Task', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}, { timestamps: false })
expect(Task.build().title).toEqual('a task!')
expect(Task.build().foo).toEqual(2)
expect(Task.build().bar).toEqual(undefined)
expect(Task.build().foobar).toEqual('asd')
expect(Task.build().flag).toEqual(false)
})
it("stores the the passed values in a special variable", function() {
var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).toEqual({ username: 'John Wayne' })
})
it("attaches getter and setter methods from attribute definition", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters1', {
price: {
type: Sequelize.INTEGER,
get : function() {
return 'answer = ' + this.getDataValue('price');
},
set : function(v) {
return this.setDataValue('price', v + 42);
}
}
},{
});
expect(Product.build({price: 42}).price).toEqual('answer = 84');
var p = Product.build({price: 1});
expect(p.price).toEqual('answer = 43');
p.price = 0;
expect(p.price).toEqual('answer = 42'); // ah finally the right answer :-)
})
it("attaches getter and setter methods from options", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters2', {
priceInCents: {
type: Sequelize.INTEGER
}
},{
setterMethods: {
price: function(value) {
this.dataValues.priceInCents = value * 100;
}
},
getterMethods: {
price: function() {
return '$' + (this.getDataValue('priceInCents') / 100);
},
priceInCents: function() {
return this.dataValues.priceInCents;
}
}
});
expect(Product.build({price: 20}).priceInCents).toEqual(20 * 100);
expect(Product.build({priceInCents: 30 * 100}).price).toEqual('$' + 30);
})
it("attaches getter and setter methods from options only if not defined in attribute", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters3', {
price1: {
type: Sequelize.INTEGER,
set : function(v) { this.setDataValue('price1', v * 10); }
},
price2: {
type: Sequelize.INTEGER,
get : function(v) { return this.getDataValue('price2') * 10; }
}
},{
setterMethods: {
price1: function(v) { this.setDataValue('price1', v * 100); }
},
getterMethods: {
price2: function() { return '$' + this.getDataValue('price2'); }
}
});
var p = Product.build({ price1: 1, price2: 2 });
expect(p.price1).toEqual(10);
expect(p.price2).toEqual(20);
})
})
describe('findOrCreate', function () {
it("Returns instace if already existent. Single find field.", function (done) {
var self = this,
data = {
username: 'Username'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate({
username: user.username
}).success(function (_user, created) {
expect(_user.id).toEqual(user.id)
expect(_user.username).toEqual('Username')
expect(created).toBeFalse()
done()
})
})
})
it("Returns instace if already existent. Multiple find fields.", function (done) {
var self = this,
data = {
username: 'Username',
data: 'ThisIsData'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate(data).success(function (_user, created) {
expect(_user.id).toEqual(user.id)
expect(_user.username).toEqual('Username')
expect(_user.data).toEqual('ThisIsData')
expect(created).toBeFalse()
done()
})
})
})
it("creates new instance with default value.", function (done) {
var self = this,
data = {
username: 'Username'
},
default_values = {
data: 'ThisIsData'
};
this.User.findOrCreate(data, default_values).success(function (user, created) {
expect(user.username).toEqual('Username')
expect(user.data).toEqual('ThisIsData')
expect(created).toBeTrue()
done()
})
})
})
describe('create', function() {
it("casts empty arrays correctly for postgresql", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').toEqual('')
return done()
}
var User = this.sequelize.define('UserWithArray', {
myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
})
User.sync({force: true}).success(function() {
User.create({myvals: [], mystr: []}).on('sql', function(sql){
expect(sql.indexOf('ARRAY[]::INTEGER[]')).toBeGreaterThan(-1)
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).toBeGreaterThan(-1)
done()
})
})
})
it("doesn't allow duplicated records with unique:true", function(done) {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: true }
})
User.sync({ force: true }).success(function() {
User.create({ username:'foo' }).success(function() {
User.create({ username: 'foo' }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: /.*Duplicate\ entry.*/,
postgres: /.*duplicate\ key\ value.*/
})
done()
})
})
})
})
it("raises an error if created object breaks definition contraints", function(done) {
var User = this.sequelize.define('UserWithNonNullSmth', {
username: { type: Sequelize.STRING, unique: true },
smth: { type: Sequelize.STRING, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({ username: 'foo', smth: null }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: "Column 'smth' cannot be null",
postgres: /.*column "smth" violates not-null.*/
})
User.create({ username: 'foo', smth: 'foo' }).success(function() {
User.create({ username: 'foo', smth: 'bar' }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: "Duplicate entry 'foo' for key 'username'",
postgres: /.*duplicate key value violates unique constraint.*/
})
done()
})
})
})
})
})
it('raises an error if you mess up the datatype', function() {
Helpers.assertException(function() {
this.sequelize.define('UserBadDataType', {
activity_date: Sequelize.DATe
})
}.bind(this), 'Unrecognized data type for field activity_date')
Helpers.assertException(function() {
this.sequelize.define('UserBadDataType', {
activity_date: {type: Sequelize.DATe}
})
}.bind(this), 'Unrecognized data type for field activity_date')
})
it('sets a 64 bit int in bigint', function(done) {
var User = this.sequelize.define('UserWithBigIntFields', {
big: Sequelize.BIGINT
})
User.sync({ force: true }).success(function() {
User.create({ big: '9223372036854775807' }).on('success', function(user) {
expect(user.big).toBe( '9223372036854775807' )
done()
})
})
})
it('sets auto increment fields', function(done) {
var User = this.sequelize.define('UserWithAutoIncrementField', {
userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({}).on('success', function(user) {
expect(user.userid).toEqual(1)
User.create({}).on('success', function(user) {
expect(user.userid).toEqual(2)
done()
})
})
})
})
it('allows the usage of options as attribute', function(done) {
var User = this.sequelize.define('UserWithNameAndOptions', {
name: Sequelize.STRING,
options: Sequelize.TEXT
})
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
User.sync({ force: true }).success(function() {
User
.create({ name: 'John Doe', options: options })
.success(function(user) {
expect(user.options).toEqual(options)
done()
})
})
})
it('allows sql logging', function(done) {
var User = this.sequelize.define('UserWithUniqueNameAndNonNullSmth', {
name: {type: Sequelize.STRING, unique: true},
smth: {type: Sequelize.STRING, allowNull: false}
})
User.sync({ force: true }).success(function() {
User
.create({ name: 'Fluffy Bunny', smth: 'else' })
.on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("INSERT")).toBeGreaterThan(-1)
done()
})
})
})
it('should only store the values passed in the whitelist', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data, ['username']).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).toEqual(data.username)
expect(_user.secretValue).not.toEqual(data.secretValue)
expect(_user.secretValue).toEqual(null)
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).toEqual(data.username)
expect(_user.secretValue).toEqual(data.secretValue)
done()
})
})
})
it('can omitt autoincremental columns', function(done) {
var self = this
, data = { title: 'Iliad' }
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
dataTypes.forEach(function(dataType, index) {
var Book = self.sequelize.define('Book'+index, {
id: { type: dataType, primaryKey: true, autoIncrement: true },
title: Sequelize.TEXT
})
Book.sync({ force: true }).success(function() {
Book
.create(data)
.success(function(book) {
expect(book.title).toEqual(data.title)
expect(book.author).toEqual(data.author)
expect(Book.rawAttributes.id.type.toString())
.toEqual(dataTypes[index].toString())
Book.drop()
if (index >= dataTypes.length - 1) {
done()
}
})
})
})
})
it('saves data with single quote', function(done) {
var quote = "single'quote"
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).toEqual(quote, 'memory single quote')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(quote, 'SQL single quote')
done()
})
})
})
it('saves data with double quote', function(done) {
var quote = 'double"quote'
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).toEqual(quote, 'memory double quote')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(quote, 'SQL double quote')
done()
})
})
})
it('saves stringified JSON data', function(done) {
var json = JSON.stringify({ key: 'value' })
, self = this
this.User.create({ data: json }).success(function(user) {
expect(user.data).toEqual(json, 'memory data')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(json, 'SQL data')
done()
})
})
})
it('stores the current date in createdAt', function(done) {
this.User.create({ username: 'foo' }).success(function(user) {
expect(parseInt(+user.createdAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
it('allows setting custom IDs', function (done) {
this.User.create({ id: 42 }).success(function (user) {
expect(user.id).toEqual(42)
this.User.find(42).success(function (user) {
expect(user).toBeDefined()
done()
})
}.bind(this))
})
describe('enums', function() {
before(function(done) {
this.Item = this.sequelize.define('Item', {
state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }
})
this.sequelize.sync({ force: true }).success(function() {
this.Item.create({ state: 'available' }).success(function(item) {
this.item = item
done()
}.bind(this))
}.bind(this))
})
it('correctly restores enum values', function(done) {
this.Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.id).toEqual(this.item.id)
done()
}.bind(this))
})
})
})
describe('bulkCreate', function() {
it('properly handles disparate field lists', function(done) {
var self = this
, data = [{username: 'Peter', secretValue: '42' },
{username: 'Paul'},
{username: 'Steve'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({where: {username: 'Paul'}}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("Paul")
expect(users[0].secretValue).toBeNull()
done()
})
})
})
it('inserts multiple values respecting the white list', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data, ['username']).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].secretValue).toBeNull();
expect(users[1].username).toEqual("Paul")
expect(users[1].secretValue).toBeNull();
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].secretValue).toEqual('42')
expect(users[1].username).toEqual("Paul")
expect(users[1].secretValue).toEqual('23')
done()
})
})
})
it('saves data with single quote', function(done) {
var self = this
, quote = "Single'Quote"
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(quote)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var self = this
, quote = 'Double"Quote'
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(quote)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var self = this
, json = JSON.stringify({ key: 'value' })
, data = [{ username: 'Peter', data: json},
{ username: 'Paul', data: json}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(json)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
var self = this
, data = [{ username: 'Peter'},
{ username: 'Paul'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(parseInt(+users[0].createdAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(users[1].username).toEqual("Paul")
expect(parseInt(+users[1].createdAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
describe('enums', function() {
before(function(done) {
this.Item = this.sequelize.define('Item', {
state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] },
name: Sequelize.STRING
})
this.sequelize.sync({ force: true }).success(function() {
this.Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() {
done()
}.bind(this))
}.bind(this))
})
it('correctly restores enum values', function(done) {
this.Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.name).toEqual('B')
done()
}.bind(this))
})
})
}) // - bulkCreate
describe('update', function() {
it('allows sql logging of updated statements', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
paranoid:true
})
this.sequelize.sync({ force: true }).success(function() {
User.create({ name: 'meg', bio: 'none' }).success(function(u) {
expect(u).toBeDefined()
expect(u).not.toBeNull()
u.updateAttributes({name: 'brian'}).on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("UPDATE")).toBeGreaterThan(-1)
done()
})
})
})
})
it('updates only values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
users.forEach(function (user) {
if (user.secretValue == '42') {
expect(user.username).toEqual("Bill")
} else {
expect(user.username).toEqual("Bob")
}
})
done()
})
})
})
})
it('sets updatedAt to the current timestamp', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
expect(users[0].username).toEqual("Bill")
expect(users[1].username).toEqual("Bill")
expect(users[2].username).toEqual("Bob")
expect(parseInt(+users[0].updatedAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(parseInt(+users[1].updatedAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
})
}) // - update
describe('destroy', function() {
it('deletes a record from the database if dao is not paranoid', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.sequelize.sync({ force: true }).success(function() {
User.create({name: 'hallo', bio: 'welt'}).success(function(u) {
User.all().success(function(users) {
expect(users.length).toEqual(1)
u.destroy().success(function() {
User.all().success(function(users) {
expect(users.length).toEqual(0)
done()
})
})
})
})
})
})
it('allows sql logging of delete statements', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.sequelize.sync({ force: true }).success(function() {
User.create({name: 'hallo', bio: 'welt'}).success(function(u) {
User.all().success(function(users) {
expect(users.length).toEqual(1)
u.destroy().on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("DELETE")).toBeGreaterThan(-1)
done()
})
})
})
})
})
it('deletes values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.destroy({secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("Bob")
done()
})
})
})
})
it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this
, User = this.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, {
paranoid: true
})
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
User.sync({ force: true }).success(function() {
User.bulkCreate(data).success(function() {
User.destroy({secretValue: '42'})
.success(function() {
User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
expect(users[0].username).toEqual("Peter")
expect(users[1].username).toEqual("Paul")
expect(users[2].username).toEqual("Bob")
expect(parseInt(+users[0].deletedAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(parseInt(+users[1].deletedAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
})
})
}) // - destroy
describe('special where conditions', function() {
before(function(done) {
var self = this
this.User.create({
username: 'boo',
intVal: 5,
theDate: '2013-01-01 12:00'
}).success(function(user){
self.user = user
self.User.create({
username: 'boo2',
intVal: 10,
theDate: '2013-01-10 12:00'
}).success(function(user2){
self.user2 = user2
done()
})
})
})
it('should be able to find a row between a certain date', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row between a certain date and an additional where clause', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
},
intVal: 10
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row not between a certain integer', function(done) {
this.User.findAll({
where: {
intVal: {
nbetween: [8, 10]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2012-12-10', '2013-01-02'],
nbetween: ['2013-01-04', '2013-01-20']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
between: [new Date('2012-12-10'), new Date('2013-01-02')],
nbetween: [new Date('2013-01-04'), new Date('2013-01-20')]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using greater than or equal to logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
gte: new Date('2013-01-09')
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than or equal to', function(done) {
this.User.find({
where: {
intVal: {
gte: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than', function(done) {
this.User.find({
where: {
intVal: {
gt: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using lesser than or equal to', function(done) {
this.User.find({
where: {
intVal: {
lte: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find a row using lesser than', function(done) {
this.User.find({
where: {
intVal: {
lt: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should have no problem finding a row using lesser and greater than', function(done) {
this.User.findAll({
where: {
intVal: {
lt: 6,
gt: 4
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not equal to logic', function(done) {
this.User.find({
where: {
intVal: {
ne: 10
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find multiple users with any of the special where logic properties', function(done) {
this.User.findAll({
where: {
intVal: {
lte: 10
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
expect(users[1].username).toEqual('boo2')
expect(users[1].intVal).toEqual(10)
done()
})
})
})
describe('find', function find() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.UserPrimary = this.sequelize.define('UserPrimary', {
specialKey: {
type: this.DataTypes.STRING,
primaryKey: true
}
})
this.UserPrimary.sync({force: true}).success(function(primary){
this.UserPrimary.create({specialKey: 'a string'}).success(function(){
this.user = user
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it('doesn\'t throw an error when entering in a non integer value for a specified primary field', function(done) {
this.UserPrimary.find('a string').success(function(user) {
expect(user.specialKey).toEqual('a string')
done()
})
})
it('doesn\'t throw an error when entering in a non integer value', function(done) {
this.User.find('a string value').success(function(user) {
expect(user).toBeNull()
done()
})
})
it('returns a single dao', function(done) {
this.User.find(this.user.id).success(function(user) {
expect(Array.isArray(user)).toBeFalsy()
expect(user.id).toEqual(this.user.id)
expect(user.id).toEqual(1)
done()
}.bind(this))
})
it('returns a single dao given a string id', function(done) {
this.User.find(this.user.id + '').success(function(user) {
expect(Array.isArray(user)).toBeFalsy()
expect(user.id).toEqual(this.user.id)
expect(user.id).toEqual(1)
done()
}.bind(this))
})
it("should make aliased attributes available", function(done) {
this.User.find({
where: { id: 1 },
attributes: ['id', ['username', 'name']]
}).success(function(user) {
expect(user.name).toEqual('barfooz')
done()
})
})
it("should not try to convert boolean values if they are not selected", function (done) {
var UserWithBoolean = this.sequelize.define('user', {
active: Sequelize.BOOLEAN
})
this.sequelize.sync({force: true}).success(function () {
UserWithBoolean.create({ active: true }).success(function (user) {
UserWithBoolean.find({ where: { id: user.id }, attributes: [ 'id' ] }).success(function (user) {
expect(user.active).not.toBeDefined()
done()
})
})
})
})
it('finds a specific user via where option', function(done) {
this.User.find({ where: { username: 'barfooz' } }).success(function(user) {
expect(user.username).toEqual('barfooz')
done()
})
})
it("doesn't find a user if conditions are not matching", function(done) {
this.User.find({ where: { username: 'foo' } }).success(function(user) {
expect(user).toBeNull()
done()
})
})
it('allows sql logging', function(done) {
this.User.find({ where: { username: 'foo' } })
.on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
it('ignores passed limit option', function(done) {
this.User.find({ limit: 10 }).success(function(user) {
// it returns an object instead of an array
expect(Array.isArray(user)).toBeFalsy()
expect(user.hasOwnProperty('username')).toBeTruthy()
done()
})
})
it('finds entries via primary keys', function(done) {
var User = this.sequelize.define('UserWithPrimaryKey', {
identifier: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING
})
User.sync({ force: true }).success(function() {
User.create({
identifier: 'an identifier',
name: 'John'
}).success(function(u) {
expect(u.id).not.toBeDefined()
User.find('an identifier').success(function(u2) {
expect(u2.identifier).toEqual('an identifier')
expect(u2.name).toEqual('John')
done()
})
})
})
})
it('returns the selected fields as instance.selectedValues', function(done) {
this.User.create({
username: 'JohnXOXOXO'
}).success(function() {
this.User.find({
where: { username: 'JohnXOXOXO' },
attributes: ['username']
}).success(function(user) {
expect(user.selectedValues).toEqual({ username: 'JohnXOXOXO' })
done()
})
}.bind(this))
})
it('returns the selected fields and all fields of the included table as instance.selectedValues', function(done) {
this.Mission = this.sequelize.define('Mission', {
title: {type: Sequelize.STRING, defaultValue: 'a mission!!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
})
this.Mission.belongsTo(this.User)
this.User.hasMany(this.Mission)
this.sequelize.sync({ force: true }).complete(function() {
this.Mission.create()
.success(function(mission) {
this.User.create({
username: 'John DOE'
}).success(function(user) {
mission.setUser(user)
.success(function() {
this.User.find({
where: { username: 'John DOE' },
attributes: ['username'],
include: [this.Mission]
}).success(function(user) {
expect(user.selectedValues).toEqual({ username: 'John DOE' })
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('always honors ZERO as primary key', function(_done) {
var permutations = [
0,
'0',
{where: {id: 0}},
{where: {id: '0'}}
]
, done = _.after(2 * permutations.length, _done);
this.User.create({username: 'jack'}).success(function (jack) {
this.User.create({username: 'jill'}).success(function (jill) {
permutations.forEach(function(perm) {
this.User.find(perm).done(function(err, user) {
expect(err).toBeNull();
expect(user).toBeNull();
done();
}).on('sql', function(s) {
expect(s.indexOf(0)).not.toEqual(-1);
done();
})
}.bind(this))
}.bind(this))
}.bind(this))
})
describe('eager loading', function() {
before(function() {
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING })
this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING })
this.init = function(callback) {
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
callback()
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this)
})
describe('belongsTo', function() {
before(function(done) {
this.Task.belongsTo(this.Worker)
this.init(function() {
this.task.setWorker(this.worker).success(done)
}.bind(this))
})
it('throws an error about unexpected input if include contains a non-object', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ 1 ] })
}.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
})
it('throws an error about missing attributes if include contains an object with daoFactory', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Worker } ] })
}.bind(this), 'Include malformed. Expected attributes: daoFactory, as!')
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('returns the associated worker via task.worker', function(done) {
this.Task.find({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, task) {
expect(err).toBeNull()
expect(task).toBeDefined()
expect(task.worker).toBeDefined()
expect(task.worker.name).toEqual('worker')
done()
}.bind(this))
})
it('returns the private and public ip', function(done) {
var Domain = this.sequelize.define('Domain', { ip: Sequelize.STRING })
var Environment = this.sequelize.define('Environment', { name: Sequelize.STRING })
Environment
.belongsTo(Domain, { as: 'PrivateDomain', foreignKey: 'privateDomainId' })
.belongsTo(Domain, { as: 'PublicDomain', foreignKey: 'publicDomainId' })
this.sequelize.sync({ force: true }).complete(function() {
Domain.create({ ip: '192.168.0.1' }).success(function(privateIp) {
Domain.create({ ip: '91.65.189.19' }).success(function(publicIp) {
Environment.create({ name: 'environment' }).success(function(env) {
env.setPrivateDomain(privateIp).success(function() {
env.setPublicDomain(publicIp).success(function() {
Environment.find({
where: { name: 'environment' },
include: [
{ daoFactory: Domain, as: 'PrivateDomain' },
{ daoFactory: Domain, as: 'PublicDomain' }
]
}).complete(function(err, environment) {
expect(err).toBeNull()
expect(environment).toBeDefined()
expect(environment.privateDomain).toBeDefined()
expect(environment.privateDomain.ip).toEqual('192.168.0.1')
expect(environment.publicDomain).toBeDefined()
expect(environment.publicDomain.ip).toEqual('91.65.189.19')
done()
})
})
})
})
})
})
})
})
})
describe('hasOne', function() {
before(function(done) {
this.Worker.hasOne(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTask(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.find({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.task).toBeDefined()
expect(worker.task.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasOne with alias', function() {
before(function(done) {
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDo(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.toDo).toBeDefined()
expect(worker.toDo.title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(worker.toDo.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany', function() {
before(function(done) {
this.Worker.hasMany(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTasks([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.find({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.tasks).toBeDefined()
expect(worker.tasks[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany with alias', function() {
before(function(done) {
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDos([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.toDos).toBeDefined()
expect(worker.toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(worker.toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
})
})
describe('queryOptions', function() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.user = user
done()
}.bind(this))
})
it("should return a DAO when queryOptions are not set", function (done) {
this.User.find({ where: { username: 'barfooz'}}).done(function (err, user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
done();
}.bind(this))
})
it("should return a DAO when raw is false", function (done) {
this.User.find({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
done();
}.bind(this))
})
it("should return raw data when raw is true", function (done) {
this.User.find({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, user) {
expect(user).not.toHavePrototype(this.User.DAO.prototype)
expect(user).toBeObject()
done();
}.bind(this))
})
}) // - describe: queryOptions
}) //- describe: find
describe('findAll', function findAll() {
describe('eager loading', function() {
before(function() {
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING })
this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING })
})
describe('belongsTo', function() {
before(function(done) {
this.Task.belongsTo(this.Worker)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.task.setWorker(this.worker).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error about unexpected input if include contains a non-object', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ 1 ] })
}.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
})
it('throws an error about missing attributes if include contains an object with daoFactory', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Worker } ] })
}.bind(this), 'Include malformed. Expected attributes: daoFactory, as!')
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('returns the associated worker via task.worker', function(done) {
this.Task.findAll({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, tasks) {
expect(err).toBeNull()
expect(tasks).toBeDefined()
expect(tasks[0].worker).toBeDefined()
expect(tasks[0].worker.name).toEqual('worker')
done()
}.bind(this))
})
})
describe('hasOne', function() {
before(function(done) {
this.Worker.hasOne(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTask(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.findAll({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].task).toBeDefined()
expect(workers[0].task.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasOne with alias', function() {
before(function(done) {
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDo(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].toDo).toBeDefined()
expect(workers[0].toDo.title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(workers[0].toDo.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany', function() {
before(function(done) {
this.Worker.hasMany(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTasks([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.findAll({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].tasks).toBeDefined()
expect(workers[0].tasks[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany with alias', function() {
before(function(done) {
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDos([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].toDos).toBeDefined()
expect(workers[0].toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(workers[0].toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('queryOptions', function() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.user = user
done()
}.bind(this))
})
it("should return a DAO when queryOptions are not set", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}).done(function (err, users) {
users.forEach(function (user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
}, this)
done();
}.bind(this))
})
it("should return a DAO when raw is false", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, users) {
users.forEach(function (user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
}, this)
done();
}.bind(this))
})
it("should return raw data when raw is true", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, users) {
users.forEach(function (user) {
expect(user).not.toHavePrototype(this.User.DAO.prototype)
expect(users[0]).toBeObject()
}, this)
done();
}.bind(this))
})
}) // - describe: queryOptions
})
describe('normal findAll', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'user', data: 'foobar', theDate: moment().toDate()}).success(function(user) {
self.User.create({username: 'user2', data: 'bar', theDate: moment().toDate()}).success(function(user2){
self.users = [user].concat(user2)
done()
})
})
})
it("finds all entries", function(done) {
this.User.findAll().on('success', function(users) {
expect(users.length).toEqual(2)
done()
})
})
it("finds all users matching the passed conditions", function(done) {
this.User.findAll({where: "id != " + this.users[1].id}).success(function(users) {
expect(users.length).toEqual(1)
done()
})
})
it("can also handle array notation", function(done) {
var self = this
this.User.findAll({where: ['id = ?', this.users[1].id]}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].id).toEqual(self.users[1].id)
done()
})
})
it("sorts the results via id in ascending order", function(done) {
this.User.findAll().success(function(users) {
expect(users.length).toEqual(2);
expect(users[0].id).toBeLessThan(users[1].id)
done()
})
})
it("sorts the results via id in descending order", function(done) {
this.User.findAll({ order: "id DESC" }).success(function(users) {
expect(users[0].id).toBeGreaterThan(users[1].id)
done()
})
})
it("sorts the results via a date column", function(done) {
var self = this
self.User.create({username: 'user3', data: 'bar', theDate: moment().add('hours', 2).toDate()}).success(function(){
self.User.findAll({ order: 'theDate DESC' }).success(function(users) {
expect(users[0].id).toBeGreaterThan(users[2].id)
done()
})
})
})
it("handles offset and limit", function(done) {
var self = this
this.User.bulkCreate([{username: 'bobby'}, {username: 'tables'}]).success(function() {
self.User.findAll({ limit: 2, offset: 2 }).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].id).toEqual(3)
done()
})
})
})
})
}) //- describe: findAll
describe('findAndCountAll', function() {
beforeEach(function(done) {
var self = this
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'},
{username: 'bobby', data: 'foo'}
]).success(function() {
self.User.all().success(function(users){
self.users = users
done()
})
})
})
it("handles where clause [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id}).success(function(info) {
expect(info.count).toEqual(2)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(2)
done()
})
})
it("handles where clause with ordering [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, order: 'id ASC'}).success(function(info) {
expect(info.count).toEqual(2)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(2)
done()
})
})
it("handles offset", function(done) {
this.User.findAndCountAll({offset: 1}).success(function(info) {
expect(info.count).toEqual(3)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(2)
done()
})
})
it("handles limit", function(done) {
this.User.findAndCountAll({limit: 1}).success(function(info) {
expect(info.count).toEqual(3)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
it("handles offset and limit", function(done) {
this.User.findAndCountAll({offset: 1, limit: 1}).success(function(info) {
expect(info.count).toEqual(3)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
})
describe('all', function() {
beforeEach(function(done) {
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'}
]).complete(done)
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).toEqual(2)
done()
})
})
})
describe('equals', function() {
it("correctly determines equality of objects", function(done) {
this.User.create({username: 'hallo', data: 'welt'}).success(function(u) {
expect(u.equals(u)).toBeTruthy()
done()
})
})
// sqlite can't handle multiple primary keys
if(dialect !== "sqlite") {
it("correctly determines equality with multiple primary keys", function(done) {
var userKeys = this.sequelize.define('userkeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.sequelize.sync({ force: true }).success(function() {
userKeys.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equals(u)).toBeTruthy()
done()
})
})
})
}
})
describe('equalsOneOf', function() {
// sqlite can't handle multiple primary keys
if (dialect !== "sqlite") {
beforeEach(function(done) {
this.userKey = this.sequelize.define('userKeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.sequelize.sync({ force: true }).success(done)
})
it('determines equality if one is matching', function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([u, {a: 1}])).toBeTruthy()
done()
})
})
it("doesn't determine equality if none is matching", function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([{b: 2}, {a: 1}])).toBeFalsy()
done()
})
})
}
})
describe('count', function() {
it('counts all created objects', function(done) {
var self = this
this.User.create({username: 'user1'}).success(function() {
self.User.create({username: 'user2'}).success(function() {
self.User.count().success(function(count) {
expect(count).toEqual(2)
done()
})
})
})
})
it('allows sql logging', function(done) {
this.User.count().on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
it('filters object', function(done) {
var self = this
this.User.create({username: 'user1'}).success(function() {
self.User.create({username: 'foo'}).success(function() {
self.User.count({where: "username LIKE '%us%'"}).success(function(count) {
expect(count).toEqual(1)
done()
})
})
})
})
})
describe('min', function() {
before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function(){
this.UserWithDec.sync({ force: true }).success(done)
}.bind(this))
})
it("should return the min value", function(done) {
this.UserWithAge.create({ age: 2 }).success(function() {
this.UserWithAge.create({ age: 3 }).success(function() {
this.UserWithAge.min('age').success(function(min) {
expect(min).toEqual(2)
done()
})
}.bind(this))
}.bind(this))
})
it('allows sql logging', function(done) {
this.UserWithAge.min('age').on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
it("should allow decimals in min", function(done){
this.UserWithDec.create({value: 3.5}).success(function(){
this.UserWithDec.create({ value: 5.5 }).success(function(){
this.UserWithDec.min('value').success(function(min){
expect(min).toEqual(3.5)
done()
})
}.bind(this))
}.bind(this))
})
}) //- describe: min
describe('max', function() {
before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER,
order: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function(){
this.UserWithDec.sync({ force: true }).success(done)
}.bind(this))
})
it("should return the max value for a field named the same as an SQL reserved keyword", function(done) {
this.UserWithAge.create({age: 3, order: 5}).success(function(){
this.UserWithAge.max('order').success(function(max) {
expect(max).toEqual(5)
done()
})
}.bind(this))
})
it("should return the max value", function(done) {
this.UserWithAge.create({ age: 2 }).success(function() {
this.UserWithAge.create({ age: 3 }).success(function() {
this.UserWithAge.max('age').success(function(max) {
expect(max).toEqual(3)
done()
})
}.bind(this))
}.bind(this))
})
it("should allow decimals in max", function(done){
this.UserWithDec.create({value: 3.5}).success(function(){
this.UserWithDec.create({ value: 5.5 }).success(function(){
this.UserWithDec.max('value').success(function(max){
expect(max).toEqual(5.5)
done()
})
}.bind(this))
}.bind(this))
})
it('allows sql logging', function(done) {
this.UserWithAge.max('age').on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
}) //- describe: max
describe('schematic support', function() {
before(function(done){
var self = this;
this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER
})
this.UserSpecial = this.sequelize.define('UserSpecial', {
age: Sequelize.INTEGER
})
self.sequelize.dropAllSchemas().success(function(){
self.sequelize.createSchema('schema_test').success(function(){
self.sequelize.createSchema('special').success(function(){
self.UserSpecial.schema('special').sync({force: true}).success(function(UserSpecialSync){
self.UserSpecialSync = UserSpecialSync;
done()
})
})
})
})
})
it("should be able to list schemas", function(done){
this.sequelize.showAllSchemas().success(function(schemas){
expect(schemas).toBeDefined()
expect(schemas[0]).toBeArray()
expect(schemas[0].length).toEqual(2)
done()
})
})
if (dialect === "mysql") {
it("should take schemaDelimiter into account if applicable", function(done){
var UserSpecialUnderscore = this.sequelize.define('UserSpecialUnderscore', {age: Sequelize.INTEGER}, {schema: 'hello', schemaDelimiter: '_'})
var UserSpecialDblUnderscore = this.sequelize.define('UserSpecialDblUnderscore', {age: Sequelize.INTEGER})
UserSpecialUnderscore.sync({force: true}).success(function(User){
UserSpecialDblUnderscore.schema('hello', '__').sync({force: true}).success(function(DblUser){
DblUser.create({age: 3}).on('sql', function(dblSql){
User.create({age: 3}).on('sql', function(sql){
expect(dblSql).toBeDefined()
expect(dblSql.indexOf('INSERT INTO `hello__UserSpecialDblUnderscores`')).toBeGreaterThan(-1)
expect(sql).toBeDefined()
expect(sql.indexOf('INSERT INTO `hello_UserSpecialUnderscores`')).toBeGreaterThan(-1)
done()
})
})
})
})
})
}
it("should be able to create and update records under any valid schematic", function(done){
var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){
expect(UserSpecial).toBeDefined()
expect(UserPublic).toBeDefined()
if (dialect === "postgres") {
expect(self.UserSpecialSync.getTableName()).toEqual('"special"."UserSpecials"');
expect(UserSpecial.indexOf('INSERT INTO "special"."UserSpecials"')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO "UserPublics"')).toBeGreaterThan(-1)
} else if (dialect === "sqlite") {
expect(self.UserSpecialSync.getTableName()).toEqual('`special`.`UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1)
} else {
expect(self.UserSpecialSync.getTableName()).toEqual('`special.UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1)
}
})
.success(function(UserSpecial){
UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){
expect(user).toBeDefined()
if (dialect === "postgres") {
expect(user.indexOf('UPDATE "special"."UserSpecials"')).toBeGreaterThan(-1)
} else {
expect(user.indexOf('UPDATE `special.UserSpecials`')).toBeGreaterThan(-1)
}
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('references', function() {
before(function() {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING })
})
describe("use of existing dao factory", function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: this.Author,
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it('references the author table', function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post.sync({ force: true }).on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).toMatch(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
} else if (dialect === 'mysql') {
expect(sql).toMatch(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
} else if (dialect === 'sqlite') {
expect(sql).toMatch(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
describe('use of table name as string', function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: 'authors',
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it('references the author table', function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post.sync({ force: true }).on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).toMatch(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
} else if (dialect === 'mysql') {
expect(sql).toMatch(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
} else if (dialect === 'sqlite') {
expect(sql).toMatch(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
describe('use of invalid table name', function() {
before(function() {
this.Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: '4uth0r5',
referencesKey: "id"
}
})
this.Author.hasMany(this.Post)
this.Post.belongsTo(this.Author)
})
it("emits the error event as the referenced table name is invalid", function(done) {
this.Author.sync({ force: true }).success(function() {
this.Post
.sync({ force: true })
.success(function() {
if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).toEqual(1)
done()
} else {
// the parser should not end up here ...
expect(2).toEqual(1)
}
}).error(function(err) {
if (dialect === 'mysql') {
expect(err.message).toMatch(/ER_CANT_CREATE_TABLE/)
} else if (dialect === 'sqlite') {
// the parser should not end up here ... see above
expect(1).toEqual(2)
} else if (dialect === 'postgres') {
expect(err.message).toMatch(/relation "4uth0r5" does not exist/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
}.bind(this))
})
})
}) //- describe: references
describe("syntax sugar", function() {
before(function(done) {
this.User = this.sequelize.define("user", {
username: Sequelize.STRING,
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
})
this.User.sync({ force: true }).success(done)
})
describe("dataset", function() {
it("returns a node-sql instance with the correct dialect", function() {
expect(this.User.dataset().sql.dialectName).toEqual(dialect)
})
it("allows me to generate sql queries", function() {
var query = this.User.dataset().select("username").toQuery()
expect(Object.keys(query)).toEqual(['text', 'values'])
})
})
describe("select", function() {
it("sets .select() as an alias to .dataset().select()", function() {
var query1 = this.User.select("username").toQuery()
, query2 = this.User.dataset().select("username").toQuery()
expect(query1.text).toEqual(query2.text)
})
})
describe("toSql", function() {
it("transforms the node-sql instance into a proper sql string", function() {
var sql = this.User.select("username").toSql()
var sqlMap = {
postgres: 'SELECT username FROM "users";',
mysql: 'SELECT `username` FROM `users`;',
sqlite: 'SELECT username FROM "users";'
}
expect(sql).toEqual(sqlMap[dialect])
})
it("transforms node-sql instances with chaining into a proper sql string", function() {
var sql = this.User.select("username").select("firstName").group("username").toSql()
var sqlMap = {
postgres: 'SELECT username, firstName FROM "users" GROUP BY username;',
mysql: 'SELECT `username`, `firstName` FROM `users` GROUP BY username;',
sqlite: 'SELECT username, firstName FROM "users" GROUP BY username;'
}
expect(sql).toEqual(sqlMap[dialect])
})
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, Sequelize = require("../index")
, config = require(__dirname + "/config/config")
, _ = require('lodash')
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAO"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1,20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
self.User2 = sequelize.define('User2', {
username: DataTypes.STRING,
updatedAt: DataTypes.DATE
}, {
timestamps: false
})
self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
self.ParanoidUser = sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, {
paranoid: true
})
self.ParanoidUser.hasOne( self.ParanoidUser )
},
onComplete: function() {
self.User.sync({ force: true }).success(function(){
self.HistoryLog.sync({ force: true }).success(function(){
self.ParanoidUser.sync({force: true }).success(done)
})
})
}
})
})
describe('Escaping', function() {
it('is done properly for special characters', function(done) {
// Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a"
// But this causes sqlite to fail and exits the entire test suite immediately
var bio = dialect + "'\"\n" // Need to add the dialect here so in case of failure I know what DB it failed for
, self = this
this.User.create({ username: bio }).success(function(u1) {
self.User.find(u1.id).success(function(u2) {
expect(u2.username).toEqual(bio)
done()
})
})
})
})
describe('isNewRecord', function() {
it('returns true for non-saved objects', function(done) {
var user = this.User.build({ username: 'user' })
expect(user.id).toBeNull()
expect(user.isNewRecord).toBeTruthy()
done()
})
it("returns false for saved objects", function(done) {
this.User.build({ username: 'user' }).save().success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
it("returns false for created objects", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
it("returns false for objects found by find method", function(done) {
var self = this
this.User.create({ username: 'user' }).success(function() {
self.User.create({ username: 'user' }).success(function(user) {
self.User.find(user.id).success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
})
})
it("returns false for objects found by findAll method", function(done) {
var chainer = new Sequelize.Utils.QueryChainer()
, self = this
for (var i = 0; i < 10; i++) {
chainer.add(self.User.create({ username: 'user' }))
}
chainer.run().success(function() {
self.User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isNewRecord).toBeFalsy()
})
done()
})
})
})
})
describe('increment', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2);
done();
});
});
});
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2);
done();
});
});
});
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).done(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).done(function (err, user3) {
user1.increment(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(3);
done();
});
});
});
});
});
});
it('should still work right with other concurrent increments', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
var _done = _.after(3, function () {
self.User.find(1).done(function (err, user2) {
expect(user2.aNumber).toEqual(6);
done();
})
});
user1.increment(['aNumber'], 2).done(_done);
user1.increment(['aNumber'], 2).done(_done);
user1.increment(['aNumber'], 2).done(_done);
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2);
done();
});
});
});
});
});
describe('decrement', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2);
done();
});
});
});
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2);
done();
});
});
});
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).done(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).done(function (err, user3) {
user1.decrement(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(-1);
done();
});
});
});
});
});
});
it('should still work right with other concurrent increments', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
var _done = _.after(3, function () {
self.User.find(1).done(function (err, user2) {
expect(user2.aNumber).toEqual(-6);
done();
})
});
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-1);
expect(user3.bNumber).toBe(-2);
done();
});
});
});
});
});
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function (done) {
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
originalUser.updateAttributes({ username: 'Doe John' }).done(function () {
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser === updatedUser).toBeTrue()
done();
})
})
})
})
it("should update the values on all references to the DAO", function (done) {
var self = this
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
self.User.find(originalUser.id).done(function (err, updater) {
updater.updateAttributes({ username: 'Doe John' }).done(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).toEqual('John Doe')
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser.username).toEqual('Doe John')
expect(updatedUser.username).toEqual('Doe John')
done();
})
})
})
})
})
it("should update read only attributes as well (updatedAt)", function (done) {
var self = this
this.timeout = 2000;
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
var originallyUpdatedAt = originalUser.updatedAt
// Wait for a second, so updatedAt will actually be different
setTimeout(function () {
self.User.find(originalUser.id).done(function (err, updater) {
updater.updateAttributes({ username: 'Doe John' }).done(function () {
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser.updatedAt).toBeGreaterThan(originallyUpdatedAt)
expect(updatedUser.updatedAt).toBeGreaterThan(originallyUpdatedAt)
done();
})
})
})
}, 1000)
})
})
it("should update the associations as well", function(done) {
var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING })
, Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
this.sequelize.sync({ force: true }).success(function() {
Book.create({ title: 'A very old book' }).success(function(book) {
Page.create({ content: 'om nom nom' }).success(function(page) {
book.setPages([ page ]).success(function() {
Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).success(function(leBook) {
page.updateAttributes({ content: 'something totally different' }).success(function(page) {
expect(leBook.pages[0].content).toEqual('om nom nom')
expect(page.content).toEqual('something totally different')
leBook.reload().success(function(leBook) {
expect(leBook.pages[0].content).toEqual('something totally different')
expect(page.content).toEqual('something totally different')
done()
})
})
})
})
})
}.bind(this))
}.bind(this))
})
});
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function() {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt instanceof Date).toBeTrue()
})
it("should store the current date in touchedAt", function() {
this.useFakeTimers().tick(5000)
var user = this.User.build({ username: 'a user'})
expect(+user.touchedAt).toBe(5000)
})
})
describe('allowNull date', function() {
it('should be just "null" and not Date with Invalid Date', function(done) {
var self = this;
this.User.build({ username: 'a user'}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue).toBe(null)
done()
})
})
})
it('should be the same valid date when saving the date', function(done) {
var self = this;
var date = new Date();
this.User.build({ username: 'a user', dateAllowNullTrue: date}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue.toString()).toEqual(date.toString())
done()
})
})
})
})
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err, result) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).toBeNull()
expect(result).toBeDefined()
done()
})
})
})
describe('save', function() {
it('only updates fields in passed array', function(done) {
var self = this
, userId = null
, date = new Date(1990, 01, 01)
this.User.create({
username: 'foo',
touchedAt: new Date()
}).success(function(user) {
user.username = 'fizz'
user.touchedAt = date
user.save(['username']).success(function(){
// re-select user
self.User.find(user.id).success(function(user2) {
// name should have changed
expect(user2.username).toEqual('fizz')
// bio should be unchanged
expect(user2.birthDate).not.toEqual(date)
done()
})
})
})
})
it("stores an entry in the database", function(done) {
var username = 'user'
, self = this
, user = this.User.build({
username: username,
touchedAt: new Date(1984, 8, 23)
})
self.User.all().success(function(users) {
expect(users.length).toEqual(0)
user.save().success(function(){
self.User.all().success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual(username)
expect(users[0].touchedAt instanceof Date).toBe(true)
expect(users[0].touchedAt).toEqual(new Date(1984, 8, 23))
done()
})
})
})
})
it("updates the timestamps", function(done) {
this.timeout = 1000 * 5;
var now = Date.now()
, user = null
, updatedAt = null
, self = this
// timeout is needed, in order to check the update of the timestamp
var build = function() {
user = self.User.build({ username: 'user' })
updatedAt = user.updatedAt
expect(updatedAt.getTime()).toBeGreaterThan(now)
setTimeout(function() {
user.save().success(function() {
expect(updatedAt.getTime()).toBeLessThan(user.updatedAt.getTime())
done()
})
}, 1000)
}
setTimeout(build, 1000)
})
describe('without timestamps option', function() {
it("doesn't update the updatedAt column", function(done) {
this.User2.create({ username: 'john doe' }).success(function(johnDoe) {
// sqlite and mysql return undefined, whereas postgres returns null
expect([undefined, null].indexOf(johnDoe.updatedAt)).not.toBe(-1);
done()
})
})
})
it('should fail a validation upon creating', function(done){
this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1);
done();
});
})
it('should fail a validation upon building', function(done){
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateCustom).toBeDefined()
expect(err.validateCustom).toBeArray()
expect(err.validateCustom[0]).toBeDefined()
expect(err.validateCustom[0]).toEqual('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done){
this.User.create({aNumber: 0}).success(function(user){
user.updateAttributes({validateTest: 'hello'}).error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeDefined()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1)
done()
})
})
})
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).toEqual(0)
done()
})
})
it('saves a record with no primary key', function(done){
this.HistoryLog.create({ someText: 'Some random text', aNumber: 3, aRandomId: 5 }).success(function(log) {
log.updateAttributes({ aNumber: 5 }).success(function(newLog){
expect(newLog.aNumber).toEqual(5)
done()
})
})
})
before(function(done) {
this.UserEager = this.sequelize.define('UserEagerLoadingSaves', {
username : Helpers.Sequelize.STRING,
age : Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.ProjectEager = this.sequelize.define('ProjectEagerLoadingSaves', {
title : Helpers.Sequelize.STRING,
overdue_days: Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.UserEager.hasMany(this.ProjectEager, { as: 'Projects' })
this.ProjectEager.belongsTo(this.UserEager, { as: 'Poobah' })
this.sequelize.sync({ force: true }).success(done)
})
it('saves one object that has a collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'joe', age: 1 }).success(function(user) {
this.ProjectEager.create({ title: 'project-joe1', overdue_days: 0 }).success(function(project1) {
this.ProjectEager.create({ title: 'project-joe2', overdue_days: 0 }).success(function(project2) {
user.setProjects([project1, project2]).success(function() {
this.UserEager.find({where: {age: 1}, include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(user) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(1)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
user.age = user.age + 1; // happy birthday joe
user.save().success(function(saveduser) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(2)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each a have collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'bart', age: 20 }).success(function(bart) {
this.UserEager.create({ username: 'lisa', age: 20 }).success(function(lisa) {
this.ProjectEager.create({ title: 'detention1', overdue_days: 0 }).success(function(detention1) {
this.ProjectEager.create({ title: 'detention2', overdue_days: 0 }).success(function(detention2) {
this.ProjectEager.create({ title: 'exam1', overdue_days: 0 }).success(function(exam1) {
this.ProjectEager.create({ title: 'exam2', overdue_days: 0 }).success(function(exam2) {
bart.setProjects([detention1, detention2]).success(function() {
lisa.setProjects([exam1, exam2]).success(function() {
this.UserEager.findAll({where: {age: 20}, order: 'username ASC', include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(simpsons) {
var _bart, _lisa;
expect(simpsons.length).toEqual(2)
_bart = simpsons[0];
_lisa = simpsons[1];
expect(_bart.projects).toBeDefined()
expect(_lisa.projects).toBeDefined()
expect(_bart.projects.length).toEqual(2)
expect(_lisa.projects.length).toEqual(2)
_bart.age = _bart.age + 1; // happy birthday bart - off to Moe's
_bart.save().success(function(savedbart) {
expect(savedbart.username).toEqual('bart')
expect(savedbart.age).toEqual(21)
_lisa.username = 'lsimpson';
_lisa.save().success(function(savedlisa) {
expect(savedlisa.username).toEqual('lsimpson')
expect(savedlisa.age).toEqual(20)
done()
})
})
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each has one eagerly loaded object (to which they belong)', function(done) {
this.UserEager.create({ username: 'poobah', age: 18 }).success(function(user) {
this.ProjectEager.create({ title: 'homework', overdue_days: 10 }).success(function(homework) {
this.ProjectEager.create({ title: 'party', overdue_days: 2 }).success(function(party) {
user.setProjects([homework, party]).success(function() {
this.ProjectEager.findAll({include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(projects) {
expect(projects.length).toEqual(2)
expect(projects[0].poobah).toBeDefined()
expect(projects[1].poobah).toBeDefined()
expect(projects[0].poobah.username).toEqual('poobah')
expect(projects[1].poobah.username).toEqual('poobah')
projects[0].title = 'partymore';
projects[1].title = 'partymore';
projects[0].overdue_days = 0;
projects[1].overdue_days = 0;
projects[0].save().success(function() {
projects[1].save().success(function() {
this.ProjectEager.findAll({where: {title: 'partymore', overdue_days: 0}, include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(savedprojects) {
expect(savedprojects.length).toEqual(2)
expect(savedprojects[0].poobah).toBeDefined()
expect(savedprojects[1].poobah).toBeDefined()
expect(savedprojects[0].poobah.username).toEqual('poobah')
expect(savedprojects[1].poobah.username).toEqual('poobah')
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('toJSON', function toJSON() {
before(function(done) {
this.User = this.sequelize.define('UserWithUsernameAndAgeAndIsAdmin', {
username: Helpers.Sequelize.STRING,
age: Helpers.Sequelize.INTEGER,
isAdmin: Helpers.Sequelize.BOOLEAN
}, { timestamps: false })
this.Project = this.sequelize.define('NiceProject', { title: Helpers.Sequelize.STRING }, { timestamps: false })
this.User.hasMany(this.Project, { as: 'Projects' })
this.Project.belongsTo(this.User, { as: 'LovelyUser' })
this.sequelize.sync({ force: true }).success(done)
})
it('returns an object containing all values', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(user.toJSON()).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
it('returns a response that can be stringified', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).toEqual('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
})
it('returns a response that can be stringified and then parsed', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.parse(JSON.stringify(user))).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
it('includes the eagerly loaded associations', function(done) {
this.User.create({ username: 'fnord', age: 1, isAdmin: true }).success(function(user) {
this.Project.create({ title: 'fnord' }).success(function(project) {
user.setProjects([ project ]).success(function() {
this.User.findAll({include: [ { model: this.Project, as: 'Projects' } ]}).success(function(users) {
var _user = users[0]
expect(_user.projects).toBeDefined()
expect(JSON.parse(JSON.stringify(_user)).projects).toBeDefined()
this.Project.findAll({include: [ { model: this.User, as: 'LovelyUser' } ]}).success(function(projects) {
var _project = projects[0]
expect(_project.lovelyUser).toBeDefined()
expect(JSON.parse(JSON.stringify(_project)).lovelyUser).toBeDefined()
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('findAll', function findAll() {
it("escapes a single single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user'name" })
.success(function() {
self.User.findAll({
where: { username: "user'name" }
}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("user'name")
done()
})
})
})
it("escapes two single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user''name" })
.success(function() {
self.User.findAll({
where: { username: "user''name" }
}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("user''name")
done()
})
})
})
it("returns the timestamps if no attributes have been specified", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
this.User.findAll().success(function(users) {
expect(users[0].createdAt).toBeDefined()
done()
}.bind(this))
}.bind(this))
})
it("does not return the timestamps if the username attribute has been specified", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
this.User.findAll({ attributes: ['username'] }).success(function(users) {
expect(users[0].createdAt).not.toBeDefined()
expect(users[0].username).toBeDefined()
done()
}.bind(this))
}.bind(this))
})
it("creates the deletedAt property, when defining paranoid as true", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
expect(users[0].deletedAt).toBeDefined()
expect(users[0].deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
})
it("sets deletedAt property to a specific date when deleting an instance", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
users[0].destroy().success(function(user) {
expect(user.deletedAt.getMonth).toBeDefined()
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("keeps the deletedAt-attribute with value null, when running updateAttributes", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
users[0].updateAttributes({username: 'newFnord'}).success(function(user) {
expect(user.deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("keeps the deletedAt-attribute with value null, when updating associations", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
this.ParanoidUser.create({ username: 'linkedFnord' }).success(function( linkedUser ) {
users[0].setParanoidUser( linkedUser ).success(function(user) {
expect(user.deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it("can reuse query option objects", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
this.User.findAll(query).success(function(users) {
expect(users[0].username).toEqual('fnord')
this.User.findAll(query).success(function(users) {
expect(users[0].username).toEqual('fnord')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('find', function find() {
it("can reuse query option objects", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
this.User.find(query).success(function(user) {
expect(user.username).toEqual('fnord')
this.User.find(query).success(function(user) {
expect(user.username).toEqual('fnord')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('equals', function find() {
it("can compare records with Date field", function(done) {
this.User.create({ username: 'fnord' }).success(function(user1) {
var query = { where: { username: 'fnord' }}
this.User.find(query).success(function(user2) {
expect(user1.equals(user2)).toBeTrue()
done()
}.bind(this))
}.bind(this))
})
})
describe('values', function() {
it('returns all values', function(done) {
var User = this.sequelize.define('UserHelper', {
username: Helpers.Sequelize.STRING
}, { timestamps: false, logging: false })
User.sync({ force: true }).success(function() {
var user = User.build({ username: 'foo' })
expect(user.values).toEqual({ username: "foo", id: null })
done()
})
})
})
describe('updateAttributes', function() {
it("updates attributes in the database", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.username).toEqual('user')
user.updateAttributes({ username: 'person' }).success(function(user) {
expect(user.username).toEqual('person')
done()
})
})
})
it("ignores unknown attributes", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.updateAttributes({ username: 'person', foo: 'bar'}).success(function(user) {
expect(user.username).toEqual('person')
expect(user.foo).not.toBeDefined()
done()
})
})
})
it("doesn't update primary keys or timestamps", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: Helpers.Sequelize.STRING,
bio: Helpers.Sequelize.TEXT,
identifier: {type: Helpers.Sequelize.STRING, primaryKey: true}
})
User.sync({ force: true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var oldCreatedAt = user.createdAt
, oldIdentifier = user.identifier
user.updateAttributes({
name: 'foobar',
createdAt: new Date(2000, 1, 1),
identifier: 'another identifier'
}).success(function(user) {
expect(user.createdAt).toEqual(oldCreatedAt)
expect(user.identifier).toEqual(oldIdentifier)
done()
})
})
})
})
it("uses primary keys in where clause", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: Helpers.Sequelize.STRING,
bio: Helpers.Sequelize.TEXT,
identifier: {type: Helpers.Sequelize.STRING, primaryKey: true}
})
User.sync({ force:true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var emitter = user.updateAttributes({name: 'foobar'})
emitter.success(function() {
expect(emitter.query.sql).toMatch(/WHERE [`"]identifier[`"]..identifier./)
done()
})
})
})
})
it('stores and restores null values', function(done) {
var Download = this.sequelize.define('download', {
startedAt: Helpers.Sequelize.DATE,
canceledAt: Helpers.Sequelize.DATE,
finishedAt: Helpers.Sequelize.DATE
})
Download.sync({ force: true }).success(function() {
Download.create({
startedAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt).toBeFalsy()
expect(download.finishedAt).toBeFalsy()
download.updateAttributes({
canceledAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt instanceof Date).toBeTrue()
expect(download.finishedAt).toBeFalsy()
Download.all({
where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL")
}).success(function(downloads) {
downloads.forEach(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt instanceof Date).toBeTrue()
expect(download.finishedAt).toBeFalsy()
})
done()
})
})
})
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("Language Util"), function() {
describe("Plural", function(){
before(function(done) {
Helpers.initTests({
dialect: dialect,
onComplete: function(sequelize) {
this.sequelize = sequelize
this.sequelize.options.language = 'es'
done()
}.bind(this)
})
})
it("should rename tables to their plural form...", function(done){
var table = this.sequelize.define('arbol', {name: Sequelize.STRING})
var table2 = this.sequelize.define('androide', {name: Sequelize.STRING})
expect(table.tableName).toEqual('arboles')
expect(table2.tableName).toEqual('androides')
done()
})
it("should be able to pluralize/singularize associations...", function(done){
var table = this.sequelize.define('arbol', {name: Sequelize.STRING})
var table2 = this.sequelize.define('androide', {name: Sequelize.STRING})
var table3 = this.sequelize.define('hombre', {name: Sequelize.STRING})
table.hasOne(table2)
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androides.identifier).toEqual('arbolId')
expect(table2.associations.arboles).toBeDefined()
expect(table3.associations.androideshombres).toBeDefined()
done()
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, Migrator = require("../lib/migrator")
}
buster.spec.expose()
buster.testRunner.timeout = 10000
describe(Helpers.getTestDialectTeaser("Migrator"), function() {
before(function(done) {
this.init = function(options, callback) {
options = Helpers.Sequelize.Utils._.extend({
path: __dirname + '/assets/migrations',
logging: function(){}
}, options || {})
var migrator = new Migrator(this.sequelize, options)
migrator
.findOrCreateSequelizeMetaDAO({ force: true })
.success(function(SequelizeMeta) {
callback && callback(migrator, SequelizeMeta)
})
.error(function(err) { console.log(err) })
}.bind(this)
Helpers.initTests({ dialect: dialect, onComplete: done, context: this })
})
it("as", function() {
expect(1).toEqual(1)
})
describe('getUndoneMigrations', function() {
it("returns no files if timestamps are after the files timestamp", function(done) {
this.init({ from: 20120101010101 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(0)
done()
}.bind(this))
}.bind(this))
})
it("returns only files between from and to", function(done) {
this.init({ from: 19700101000000, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(1)
expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js')
done()
}.bind(this))
}.bind(this))
})
it("returns exactly the migration which is defined in from and to", function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(1)
expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js')
done()
}.bind(this))
}.bind(this))
})
it("returns also the file which is exactly options.from or options.to", function(done) {
this.init({ from: 20111117063700, to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(2)
expect(migrations[0].filename).toEqual('20111117063700-createPerson.js')
expect(migrations[1].filename).toEqual('20111130161100-emptyMigration.js')
done()
}.bind(this))
}.bind(this))
})
it("returns all files to options.to if no options.from is defined", function(done) {
this.init({ to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(2)
done()
}.bind(this))
}.bind(this))
})
it("returns all files from last migration id stored in database", function(done) {
this.init(undefined, function(migrator, SequelizeMeta) {
SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(6)
expect(migrations[0].filename).toEqual('20111130161100-emptyMigration.js')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('migrations', function() {
before(function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
this.migrator = migrator
this.migrator.migrate().success(done)
}.bind(this))
})
describe('executions', function() {
it("executes migration #20111117063700 and correctly creates the table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ])
done()
})
})
it("executes migration #20111117063700 and correctly adds isBetaMember", function(done) {
this.sequelize.getQueryInterface().describeTable('Person').success(function(data) {
var fields = Helpers.Sequelize.Utils._.keys(data).sort()
expect(fields).toEqual([ 'isBetaMember', 'name' ])
done()
})
})
it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ])
this.migrator.migrate({ method: 'down' }).success(function() {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([])
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("executes the empty migration #20111130161100", function(done) {
this.init({ from: 20111130161100, to: 20111130161100 }, function(migrator) {
// this migration isn't actually testing anything but
// should not timeout
expect(1).toEqual(1)
migrator
.migrate()
.success(done)
.error(function(err) { console.log(err) })
})
})
})
describe('renameTable', function() {
before(function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
this.migrator = migrator
this.migrator.migrate().success(done)
}.bind(this))
})
it("executes migration #20111205064000 and renames a table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toContain('Person')
this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'User' ])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('addColumn', function() {
it('adds a column to the user table', function(done) {
this.init({ from: 20111117063700, to: 20111205162700 }, function(migrator) {
migrator.migrate().complete(function(err) {
this.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).toEqual(true)
expect(isAdmin.allowNull).toEqual(false)
expect(isAdmin.defaultValue).toEqual(false)
expect(shopId.allowNull).toEqual(true)
done()
})
}.bind(this))
}.bind(this))
})
})
describe('removeColumn', function() {
it('removes the shopId column from user', function(done) {
this.init({ to: 20111206061400 }, function(migrator) {
migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).toEqual(true)
expect(isAdmin.allowNull).toEqual(false)
expect(isAdmin.defaultValue).toEqual(false)
expect(shopId).toBeFalsy()
done()
})
}.bind(this))
}.bind(this))
})
})
describe('changeColumn', function() {
it('changes the signature column from user to default "signature" + notNull', function(done) {
this.init({ to: 20111206063000 }, function(migrator) {
migrator.migrate().success(function() {
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
if (dialect === 'postgres') {
expect(signature.type).toEqual('CHARACTER VARYING')
} else {
expect(signature.type).toEqual('VARCHAR(255)')
}
expect(signature.allowNull).toEqual(false)
expect(signature.defaultValue).toEqual('Signature')
done()
})
}.bind(this))
}.bind(this))
})
})
})
describe('renameColumn', function() {
it("renames the signature column from user to sig", function(done) {
this.init({ to: 20111206163300 }, function(migrator) {
migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, sig = data.sig
expect(signature).toBeFalsy()
expect(sig).toBeTruthy()
done()
})
}.bind(this))
}.bind(this))
})
})
})
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, config = require('../config/config')
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] DAOFactory', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = self.sequelize.define('User', { age: DataTypes.INTEGER, name: DataTypes.STRING, bio: DataTypes.TEXT })
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
describe('constructor', function() {
it("handles extended attributes (unique)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: { type: Helpers.Sequelize.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (default)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (null)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (comment)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (primaryKey)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) PRIMARY KEY"})
done()
})
it("adds timestamps", function(done) {
var User1 = this.sequelize.define('User' + config.rand(), {})
var User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true })
expect(User1.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
expect(User2.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("adds deletedAt if paranoid", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true })
expect(User.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("underscores timestamps if underscored", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true })
expect(User.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
done()
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
foo: {type: Helpers.Sequelize.STRING, primaryKey: true},
bar: Helpers.Sequelize.STRING
})
expect(User.primaryKeys).toEqual({"foo":"VARCHAR(255) PRIMARY KEY"})
done()
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] hstore', function() {
const hstore = require('../../lib/dialects/postgres/hstore')
describe('stringifyPart', function() {
it("handles undefined values correctly", function() {
expect(hstore.stringifyPart(undefined)).toEqual('NULL')
})
it("handles null values correctly", function() {
expect(hstore.stringifyPart(null)).toEqual('NULL')
})
it("handles boolean values correctly", function() {
expect(hstore.stringifyPart(false)).toEqual('false')
expect(hstore.stringifyPart(true)).toEqual('true')
})
it("handles strings correctly", function() {
expect(hstore.stringifyPart('foo')).toEqual('"foo"')
})
it("handles strings with backslashes correctly", function() {
expect(hstore.stringifyPart("\\'literally\\'")).toEqual('"\\\\\'literally\\\\\'"')
})
it("handles arrays correctly", function() {
expect(hstore.stringifyPart([1,['2'],'"3"'])).toEqual('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
})
it("handles simple objects correctly", function() {
expect(hstore.stringifyPart({ test: 'value' })).toEqual('"{\\"test\\":\\"value\\"}"')
})
it("handles nested objects correctly", function () {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).toEqual('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
})
it("handles objects correctly", function() {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).toEqual('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function() {
expect(hstore.stringify({ })).toEqual('')
})
it('should handle null values correctly', function () {
expect(hstore.stringify({ null: null })).toEqual('"null"=>NULL')
})
it('should handle simple objects correctly', function() {
expect(hstore.stringify({ test: 'value' })).toEqual('"test"=>"value"')
})
it('should handle nested objects correctly', function() {
expect(hstore.stringify({ test: { nested: 'value' } })).toEqual('"test"=>"{\\"nested\\":\\"value\\"}"')
})
it('should handle nested arrays correctly', function() {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).toEqual('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).toEqual('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
})
})
describe('parse', function() {
it('should handle empty objects correctly', function() {
expect(hstore.parse('')).toEqual({ })
})
it('should handle simple objects correctly', function() {
expect(hstore.parse('"test"=>"value"')).toEqual({ test: 'value' })
})
it('should handle nested objects correctly', function() {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).toEqual({ test: { nested: 'value' } })
})
it('should handle nested arrays correctly', function() {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).toEqual({ test: [ 1, '2', [ '"3"' ] ] })
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).toEqual({ true: true, false: false, null: null, undefined: null, integer: 1, array: [1,'2'], object: { object: 'value' }})
})
})
})
}
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Promise"), function () {
before(function (done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function (sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1, 20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
self.ParanoidUser = sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, {
paranoid: true
})
self.ParanoidUser.hasOne(self.ParanoidUser)
},
onComplete: function () {
self.User.sync({ force: true }).then(function () {
return self.HistoryLog.sync({ force: true })
}).then(function () {
return self.ParanoidUser.sync({force: true })
})
.then(function () {done()}, done)
}
})
})
describe('increment', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment(['aNumber'], 2)
}).then(function (user2) {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(2);
done();
}, done);
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
// Select the user again (simulating a concurrent query)
return self.User.find(1).then(function (user2) {
return user2.updateAttributes({
aNumber: user2.aNumber + 1
}).then(function (user3) {
return user1.increment(['aNumber'], 2)
}).then(function (user4) {
return self.User.find(1)
}).then(function (user5) {
expect(user5.aNumber).toBe(3);
done();
}, done)
});
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment({ 'aNumber': 1, 'bNumber': 2})
}).then(function () {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2);
done();
}, done);
});
});
describe('decrement', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('should still work right with other concurrent decrements', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
var _done = _.after(3, function () {
self.User.find(1).then(function (user2) {
expect(user2.aNumber).toEqual(-6);
done();
})
});
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
});
});
});
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function (done) {
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return originalUser.updateAttributes({ username: 'Doe John' }).then(function () {
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser === updatedUser).toBeTrue()
done();
}, done)
})
})
it("should update the values on all references to the DAO", function (done) {
var self = this
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return self.User.find(originalUser.id).then(function (updater) {
return updater.updateAttributes({ username: 'Doe John' })
}).then(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).toEqual('John Doe')
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser.username).toEqual('Doe John')
expect(updatedUser.username).toEqual('Doe John')
done();
}, done)
})
})
it("should update the associations as well", function (done) {
var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING })
, Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
this.sequelize.sync({ force: true }).then(function () {
return Book.create({ title: 'A very old book' })
}).then(function (book) {
return Page.create({ content: 'om nom nom' }).then(function (page) {
return book.setPages([ page ]).then(function () {
return Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).then(function (leBook) {
return page.updateAttributes({ content: 'something totally different' }).then(function (page) {
expect(leBook.pages[0].content).toEqual('om nom nom')
expect(page.content).toEqual('something totally different')
return leBook.reload().then(function (leBook) {
expect(leBook.pages[0].content).toEqual('something totally different')
expect(page.content).toEqual('something totally different')
done()
})
})
})
})
})
}, done)
})
});
describe('complete', function () {
it("gets triggered if an error occurs", function (done) {
this.User.find({ where: "asdasdasd" }).then(null, function (err) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function (done) {
this.User.count().then(function (result) {
expect(result).toBeDefined()
done()
})
})
})
describe('save', function () {
it('should fail a validation upon creating', function (done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1);
done();
});
})
it('should fail a validation upon building', function (done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateCustom).toBeDefined()
expect(err.validateCustom).toBeArray()
expect(err.validateCustom[0]).toBeDefined()
expect(err.validateCustom[0]).toEqual('Length failed.')
done()
})
})
it('should fail a validation when updating', function (done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeDefined()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1)
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("QueryInterface"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: function() {
this.interface = this.sequelize.getQueryInterface()
done()
}.bind(this)
})
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
this.interface.createTable('table', { name: Helpers.Sequelize.STRING }).complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(1)
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('indexes', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN
}).success(done)
})
it('adds, reads and removes an index to the table', function(done) {
this.interface.addIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
var indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual(['user_username_is_admin'])
this.interface.removeIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual([])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('describeTable', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN,
enumVals: Helpers.Sequelize.ENUM('hello', 'world')
}).success(done)
})
it('reads the metadata of the table', function(done) {
this.interface.describeTable('User').complete(function(err, metadata) {
expect(err).toBeNull()
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).toEqual(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).toBeTrue()
expect(username.defaultValue).toBeNull()
expect(isAdmin.type).toEqual(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).toBeTrue()
expect(isAdmin.defaultValue).toBeNull()
if (dialect === 'postgres') {
expect(enumVals.special).toBeArray();
expect(enumVals.special.length).toEqual(2);
}
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect()
, moment = require('moment')
}
var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') {
return '"' + str + '"'
} else if (dialect == 'mysql') {
return '`' + str + '`'
} else {
return str
}
}
buster.spec.expose()
buster.timeout = 1000
describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { this.sequelize = sequelize }.bind(this),
onComplete: done
})
})
describe('constructor', function() {
it('should pass the global options correctly', function(done) {
var sequelize = Helpers.createSequelizeInstance({ logging: false, define: { underscored:true } })
, DAO = sequelize.define('dao', {name: Helpers.Sequelize.STRING})
expect(DAO.options.underscored).toBeTruthy()
done()
})
it('should correctly set the host and the port', function(done) {
var sequelize = Helpers.createSequelizeInstance({ host: '127.0.0.1', port: 1234 })
expect(sequelize.config.port).toEqual(1234)
expect(sequelize.config.host).toEqual('127.0.0.1')
done()
})
})
describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).toBeFalse()
})
it("returns true if the dao was defined before", function() {
this.sequelize.define('Project', {
name: Helpers.Sequelize.STRING
})
expect(this.sequelize.isDefined('Project')).toBeTrue()
})
})
describe('query', function() {
before(function(done) {
this.User = this.sequelize.define('User', {
username: Helpers.Sequelize.STRING
})
this.insertQuery = "INSERT INTO " + qq(this.User.tableName) + " (username, " + qq("createdAt") + ", " + qq("updatedAt") + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"
this.User.sync().success(done).error(function(err) {
console.log(err)
done()
})
})
it('executes a query the internal way', function(done) {
this.sequelize.query(this.insertQuery, null, { raw: true })
.complete(function(err, result) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(result).toBeNull()
done()
})
})
it('executes a query if only the sql is passed', function(done) {
this.sequelize.query(this.insertQuery)
.complete(function(err, result) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(result).not.toBeDefined()
done()
})
})
it('executes select queries correctly', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select * from " + qq(this.User.tableName) + "")
.complete(function(err, users) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
})
it('executes select queries correctly when quoteIdentifiers is false', function(done) {
this.sequelize.options.quoteIdentifiers = false
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select * from " + qq(this.User.tableName) + "")
.complete(function(err, users) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
})
it('executes select query and parses dot notation results', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select username as " + qq("user.username") + " from " + qq(this.User.tableName) + "")
.complete(function(err, users) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(users.map(function(u){ return u.user })).toEqual([{'username':'john'}])
done()
})
}.bind(this))
})
if (dialect == 'mysql') {
it('executes stored procedures', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() {
this.sequelize.query(
"CREATE PROCEDURE foo()\nSELECT * FROM " + this.User.tableName + ";"
).success(function() {
this.sequelize.query('CALL foo()').success(function(users) {
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
} else {
console.log('FIXME: I want to be supported in this dialect as well :-(')
}
it('uses the passed DAOFactory', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query("SELECT * FROM " + qq(this.User.tableName) + ";", this.User).success(function(users) {
expect(users[0].__factory).toEqual(this.User)
done()
}.bind(this))
}.bind(this))
})
it('destructs dot separated attributes when doing a raw query', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`'
, sql = "select 1 as " + Helpers.Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true }).success(function(result) {
expect(result).toEqual([ { foo: { bar: { baz: 1 } } } ])
done()
})
})
it('replaces token with the passed array', function(done) {
this.sequelize.query('select ? as foo, ? as bar', null, { raw: true }, [ 1, 2 ]).success(function(result) {
expect(result).toEqual([{ foo: 1, bar: 2 }])
done()
})
})
it('handles AS in conjunction with functions just fine', function(done) {
this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).toBeTrue()
done()
})
})
})
describe('define', function() {
it("adds a new dao to the dao manager", function(done) {
expect(this.sequelize.daoFactoryManager.all.length).toEqual(0)
this.sequelize.define('foo', { title: Helpers.Sequelize.STRING })
expect(this.sequelize.daoFactoryManager.all.length).toEqual(1)
done()
})
it("overwrites global options", function(done) {
var sequelize = Helpers.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Helpers.Sequelize.STRING}, {collate: 'utf8_bin'})
expect(DAO.options.collate).toEqual('utf8_bin')
done()
})
it("inherits global collate option", function(done) {
var sequelize = Helpers.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Helpers.Sequelize.STRING})
expect(DAO.options.collate).toEqual('utf8_general_ci')
done()
})
it("inherits global classMethods and instanceMethods", function(done) {
var sequelize = Helpers.createSequelizeInstance({
define: {
classMethods : { globalClassMethod : function() {} },
instanceMethods : { globalInstanceMethod : function() {} }
}
})
var DAO = sequelize.define('foo', {bar: Helpers.Sequelize.STRING}, {
classMethods : { localClassMethod : function() {} }
})
expect(typeof DAO.options.classMethods.globalClassMethod).toEqual('function')
expect(typeof DAO.options.classMethods.localClassMethod).toEqual('function')
expect(typeof DAO.options.instanceMethods.globalInstanceMethod).toEqual('function')
done()
})
it("uses the passed tableName", function(done) {
var self = this
, Photo = this.sequelize.define('Foto', { name: Helpers.Sequelize.STRING }, { tableName: 'photos' })
Photo.sync({ force: true }).success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
expect(tableNames).toContain('photos')
done()
})
})
})
})
describe('sync', function() {
it("synchronizes all daos", function(done) {
var Project = this.sequelize.define('project' + config.rand(), { title: Helpers.Sequelize.STRING })
var Task = this.sequelize.define('task' + config.rand(), { title: Helpers.Sequelize.STRING })
this.sequelize.sync().success(function() {
Project.create({title: 'bla'}).success(function() {
Task.create({title: 'bla'}).success(function(task){
expect(task).toBeDefined()
expect(task.title).toEqual('bla')
done()
})
})
})
})
it('works with correct database credentials', function(done) {
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
User.sync().success(function() {
expect(true).toBeTrue()
done()
})
})
it("fails with incorrect database credentials", function(done) {
var sequelize2 = Helpers.getSequelizeInstance('foo', 'bar', null, { logging: false })
, User2 = sequelize2.define('User', { name: Helpers.Sequelize.STRING, bio: Helpers.Sequelize.TEXT })
User2.sync().error(function(err) {
expect(err.message).toMatch(/.*Access\ denied.*/)
done()
})
})
})
describe('drop should work', function() {
it('correctly succeeds', function(done) {
var User = this.sequelize.define('Users', {username: Helpers.Sequelize.STRING })
User.sync({ force: true }).success(function() {
User.drop().success(function() {
expect(true).toBeTrue()
done()
})
})
})
})
describe('import', function() {
it("imports a dao definition from a file", function(done) {
var Project = this.sequelize.import(__dirname + "/assets/project")
expect(Project).toBeDefined()
done()
})
})
describe('define', function() {
[
{ type: Helpers.Sequelize.ENUM, values: ['scheduled', 'active', 'finished']},
Helpers.Sequelize.ENUM('scheduled', 'active', 'finished')
].forEach(function(status) {
describe('enum', function() {
before(function(done) {
this.Review = this.sequelize.define('review', { status: status })
this.Review.sync({ force: true }).success(done)
})
it('raises an error if no values are defined', function() {
Helpers.assertException(function() {
this.sequelize.define('omnomnom', {
bla: { type: Helpers.Sequelize.ENUM }
})
}.bind(this), 'Values for ENUM haven\'t been defined.')
})
it('correctly stores values', function(done) {
this.Review.create({ status: 'active' }).success(function(review) {
expect(review.status).toEqual('active')
done()
})
})
it('correctly loads values', function(done) {
this.Review.create({ status: 'active' }).success(function() {
this.Review.findAll().success(function(reviews) {
expect(reviews[0].status).toEqual('active')
done()
})
}.bind(this))
})
it("doesn't save an instance if value is not in the range of enums", function() {
Helpers.assertException(function() {
this.Review.create({ status: 'fnord' })
}.bind(this), 'Value "fnord" for ENUM status is out of allowed scope. Allowed values: scheduled, active, finished')
})
})
})
describe('table', function() {
[
{ id: { type: Helpers.Sequelize.BIGINT } },
{ id: { type: Helpers.Sequelize.STRING, allowNull: true } },
{ id: { type: Helpers.Sequelize.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].forEach(function(customAttributes) {
it('should be able to override options on the default attributes', function(done) {
var Picture = this.sequelize.define('picture', Helpers.Sequelize.Utils._.cloneDeep(customAttributes))
Picture.sync({ force: true }).success(function() {
Object.keys(customAttributes).forEach(function(attribute) {
Object.keys(customAttributes[attribute]).forEach(function(option) {
var optionValue = customAttributes[attribute][option];
expect(Picture.rawAttributes[attribute][option]).toBe(optionValue)
});
})
done()
})
})
})
})
})
})
module.exports = {
up: function(migration, DataTypes, done) {
migration.addColumn('User', 'uniqueName', { type: DataTypes.STRING }).complete(function() {
migration.changeColumn('User', 'uniqueName', { type: DataTypes.STRING, allowNull: false, unique: true }).complete(done)
})
},
down: function(migration, DataTypes, done) {
migration.removeColumn('User', 'uniqueName').complete(done)
}
}
...@@ -6,7 +6,7 @@ module.exports = { ...@@ -6,7 +6,7 @@ module.exports = {
pool: { maxConnections: 5, maxIdleTime: 30000}, pool: { maxConnections: 5, maxIdleTime: 30000},
rand: function() { rand: function() {
return parseInt(Math.random() * 999) return parseInt(Math.random() * 999, 10)
}, },
//make maxIdleTime small so that tests exit promptly //make maxIdleTime small so that tests exit promptly
...@@ -26,6 +26,6 @@ module.exports = { ...@@ -26,6 +26,6 @@ module.exports = {
database: 'sequelize_test', database: 'sequelize_test',
username: "postgres", username: "postgres",
port: 5432, port: 5432,
pool: { maxConnections: 5, maxIdleTime: 30} pool: { maxConnections: 5, maxIdleTime: 3000}
} }
} }
var chai = require('chai')
, expect = chai.expect
, config = require(__dirname + "/config/config")
, Support = require(__dirname + '/support')
, dialect = Support.getTestDialect()
, Sequelize = require(__dirname + '/../index')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it("when we don't have the correct server details", function(done) {
// mysql is not properly supported due to the custom pooling system
if (dialect !== "postgres" && dialect !== "postgres-native") {
console.log('This dialect doesn\'t support me :(')
expect(true).to.be.true // Silence Buster
return done()
}
var seq = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
seq.query('select 1 as hello').error(function(err) {
expect(err.message).to.match(/Failed to find (.*?) Please double check your settings\./)
done()
})
})
it('when we don\'t have the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native") {
console.log('This dialect doesn\'t support me :(')
expect(true).to.be.true // Silence Buster
return done()
}
var seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
seq.query('select 1 as hello').error(function(err) {
expect(err.message).to.match(/^Failed to authenticate/)
done()
})
})
it('when we don\'t have a valid dialect.', function(done) {
expect(function() {
new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {host: '0.0.0.1', port: config[dialect].port, dialect: undefined})
}).to.throw(Error, 'The dialect undefined is not supported.')
done()
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
var config = sequelize.config
var options = sequelize.options
expect(options.dialect).to.equal('mysql')
expect(config.database).to.equal('dbname')
expect(config.host).to.equal('example.com')
expect(config.username).to.equal('user')
expect(config.password).to.equal('pass')
expect(config.port).to.equal('9821')
})
it('should work with no authentication options', function(done) {
var sequelize = new Sequelize('mysql://example.com:9821/dbname')
var config = sequelize.config
expect(config.username).to.not.be.ok
expect(config.password).to.be.null
done()
})
it('should use the default port when no other is specified', function(done) {
var sequelize = new Sequelize('mysql://example.com/dbname')
var config = sequelize.config
// The default port should be set
expect(config.port).to.equal(3306)
done()
})
})
describe('Intantiation with arguments', function() {
it('should accept two parameters (database, username)', function(done) {
var sequelize = new Sequelize('dbname', 'root')
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
done()
})
it('should accept three parameters (database, username, password)', function(done) {
var sequelize = new Sequelize('dbname', 'root', 'pass')
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
expect(config.password).to.equal('pass')
done()
})
it('should accept four parameters (database, username, password, options)', function(done) {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
expect(config.password).to.equal('pass')
expect(config.port).to.equal(999)
done()
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, Sequelize = require('../index')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAOFactory"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe('constructor', function() {
it("uses the passed dao name as tablename if freezeTableName", function(done) {
var User = this.sequelize.define('FrozenUser', {}, { freezeTableName: true })
expect(User.tableName).to.equal('FrozenUser')
done()
})
it("uses the pluralized dao name as tablename unless freezeTableName", function(done) {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
expect(User.tableName).to.equal('SuperUsers')
done()
})
it("uses checks to make sure dao factory isnt leaking on multiple define", function(done) {
this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize = this.sequelize.daoFactoryManager.all.length
this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize2 = this.sequelize.daoFactoryManager.all.length
expect(factorySize).to.equal(factorySize2)
done()
})
it("attaches class and instance methods", function(done) {
var User = this.sequelize.define('UserWithClassAndInstanceMethods', {}, {
classMethods: { doSmth: function(){ return 1 } },
instanceMethods: { makeItSo: function(){ return 2}}
})
expect(User.doSmth).to.exist
expect(User.doSmth()).to.equal(1)
expect(User.makeItSo).not.to.exist
expect(User.build().doSmth).not.to.exist
expect(User.build().makeItSo).to.exist
expect(User.build().makeItSo()).to.equal(2)
done()
})
it("throws an error if 2 autoIncrements are passed", function(done) {
var self = this
expect(function() {
self.sequelize.define('UserWithTwoAutoIncrements', {
userid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
userscore: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }
})
}).to.throw(Error, 'Invalid DAO definition. Only one autoincrement field allowed.')
done()
})
it('throws an error if a custom model-wide validation is not a function', function(done) {
var self = this
expect(function() {
self.sequelize.define('Foo', {
field: Sequelize.INTEGER
}, {
validate: {
notFunction: 33
}
})
}).to.throw(Error, 'Members of the validate option must be functions. Model: Foo, error with validate member notFunction')
done()
})
it('throws an error if a custom model-wide validation has the same name as a field', function(done) {
var self = this
expect(function() {
self.sequelize.define('Foo', {
field: Sequelize.INTEGER
}, {
validate: {
field: function() {}
}
})
}).to.throw(Error, 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field')
done()
})
})
describe('build', function() {
it("doesn't create database entries", function(done) {
this.User.build({ username: 'John Wayne' })
this.User.all().success(function(users) {
expect(users).to.have.length(0)
done()
})
})
it("fills the objects with default values", function(done) {
var Task = this.sequelize.define('TaskBuild', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
})
expect(Task.build().title).to.equal('a task!')
expect(Task.build().foo).to.equal(2)
expect(Task.build().bar).to.not.be.ok
expect(Task.build().foobar).to.equal('asd')
expect(Task.build().flag).to.be.false
done()
})
it("fills the objects with default values", function(done) {
var Task = this.sequelize.define('TaskBuild', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}, { timestamps: false })
expect(Task.build().title).to.equal('a task!')
expect(Task.build().foo).to.equal(2)
expect(Task.build().bar).to.not.be.ok
expect(Task.build().foobar).to.equal('asd')
expect(Task.build().flag).to.be.false
done()
})
it("stores the the passed values in a special variable", function(done) {
var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).to.deep.equal({ username: 'John Wayne' })
done()
})
it("attaches getter and setter methods from attribute definition", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters1', {
price: {
type: Sequelize.INTEGER,
get : function() {
return 'answer = ' + this.getDataValue('price')
},
set : function(v) {
return this.setDataValue('price', v + 42)
}
}
})
expect(Product.build({price: 42}).price).to.equal('answer = 84')
var p = Product.build({price: 1})
expect(p.price).to.equal('answer = 43');
p.price = 0
expect(p.price).to.equal('answer = 42')
done()
})
it("attaches getter and setter methods from options", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters2', {
priceInCents: Sequelize.INTEGER
},{
setterMethods: {
price: function(value) {
this.dataValues.priceInCents = value * 100
}
},
getterMethods: {
price: function() {
return '$' + (this.getDataValue('priceInCents') / 100)
},
priceInCents: function() {
return this.dataValues.priceInCents
}
}
});
expect(Product.build({price: 20}).priceInCents).to.equal(20 * 100)
expect(Product.build({priceInCents: 30 * 100}).price).to.equal('$' + 30)
done()
})
it("attaches getter and setter methods from options only if not defined in attribute", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters3', {
price1: {
type: Sequelize.INTEGER,
set : function(v) { this.setDataValue('price1', v * 10) }
},
price2: {
type: Sequelize.INTEGER,
get : function() { return this.getDataValue('price2') * 10 }
}
},{
setterMethods: {
price1: function(v) { this.setDataValue('price1', v * 100) }
},
getterMethods: {
price2: function() { return '$' + this.getDataValue('price2') }
}
});
var p = Product.build({ price1: 1, price2: 2 })
expect(p.price1).to.equal(10)
expect(p.price2).to.equal(20)
done()
})
})
describe('findOrCreate', function () {
it("Returns instace if already existent. Single find field.", function(done) {
var self = this,
data = {
username: 'Username'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate({
username: user.username
}).success(function (_user, created) {
expect(_user.id).to.equal(user.id)
expect(_user.username).to.equal('Username')
expect(created).to.be.false
done()
})
})
})
it("Returns instace if already existent. Multiple find fields.", function(done) {
var self = this,
data = {
username: 'Username',
data: 'ThisIsData'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate(data).success(function (_user, created) {
expect(_user.id).to.equal(user.id)
expect(_user.username).to.equal('Username')
expect(_user.data).to.equal('ThisIsData')
expect(created).to.be.false
done()
})
})
})
it("creates new instance with default value.", function(done) {
var data = {
username: 'Username'
},
default_values = {
data: 'ThisIsData'
};
this.User.findOrCreate(data, default_values).success(function(user, created) {
expect(user.username).to.equal('Username')
expect(user.data).to.equal('ThisIsData')
expect(created).to.be.true
done()
})
})
})
describe('create', function() {
it("casts empty arrays correctly for postgresql", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').to.equal('')
return done()
}
var User = this.sequelize.define('UserWithArray', {
myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
})
User.sync({force: true}).success(function() {
User.create({myvals: [], mystr: []}).on('sql', function(sql){
expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1)
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1)
done()
})
})
})
it("doesn't allow duplicated records with unique:true", function(done) {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: true }
})
User.sync({ force: true }).success(function() {
User.create({ username:'foo' }).success(function() {
User.create({ username: 'foo' }).error(function(err) {
expect(err).to.exist
if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
}
else if (dialect === "mysql") {
expect(err.message).to.match(/.*Duplicate\ entry.*/)
} else {
expect(err.message).to.match(/.*duplicate\ key\ value.*/)
}
done()
})
})
})
})
it("raises an error if created object breaks definition contraints", function(done) {
var UserNull = this.sequelize.define('UserWithNonNullSmth', {
username: { type: Sequelize.STRING, unique: true },
smth: { type: Sequelize.STRING, allowNull: false }
})
UserNull.sync({ force: true }).success(function() {
UserNull.create({ username: 'foo', smth: null }).error(function(err) {
expect(err).to.exist
if (dialect === "mysql") {
// We need to allow two different errors for MySQL, see:
// http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_trans_tables
expect(err.message).to.match(/(Column 'smth' cannot be null|Field 'smth' doesn't have a default value)/)
}
else if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
} else {
expect(err.message).to.match(/.*column "smth" violates not-null.*/)
}
UserNull.create({ username: 'foo', smth: 'foo' }).success(function() {
UserNull.create({ username: 'foo', smth: 'bar' }).error(function(err) {
expect(err).to.exist
if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
}
else if (dialect === "mysql") {
expect(err.message).to.match(/Duplicate entry 'foo' for key 'username'/)
} else {
expect(err.message).to.match(/.*duplicate key value violates unique constraint.*/)
}
done()
})
})
})
})
})
it('raises an error if you mess up the datatype', function(done) {
var self = this
expect(function() {
self.sequelize.define('UserBadDataType', {
activity_date: Sequelize.DATe
})
}).to.throw(Error, 'Unrecognized data type for field activity_date')
expect(function() {
self.sequelize.define('UserBadDataType', {
activity_date: {type: Sequelize.DATe}
})
}).to.throw(Error, 'Unrecognized data type for field activity_date')
done()
})
it('sets a 64 bit int in bigint', function(done) {
var User = this.sequelize.define('UserWithBigIntFields', {
big: Sequelize.BIGINT
})
User.sync({ force: true }).success(function() {
User.create({ big: '9223372036854775807' }).on('success', function(user) {
expect(user.big).to.be.equal( '9223372036854775807' )
done()
})
})
})
it('sets auto increment fields', function(done) {
var User = this.sequelize.define('UserWithAutoIncrementField', {
userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({}).on('success', function(user) {
expect(user.userid).to.equal(1)
User.create({}).on('success', function(user) {
expect(user.userid).to.equal(2)
done()
})
})
})
})
it('allows the usage of options as attribute', function(done) {
var User = this.sequelize.define('UserWithNameAndOptions', {
name: Sequelize.STRING,
options: Sequelize.TEXT
})
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
User.sync({ force: true }).success(function() {
User
.create({ name: 'John Doe', options: options })
.success(function(user) {
expect(user.options).to.equal(options)
done()
})
})
})
it('allows sql logging', function(done) {
var User = this.sequelize.define('UserWithUniqueNameAndNonNullSmth', {
name: {type: Sequelize.STRING, unique: true},
smth: {type: Sequelize.STRING, allowNull: false}
})
User.sync({ force: true }).success(function() {
User
.create({ name: 'Fluffy Bunny', smth: 'else' })
.on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("INSERT")).to.be.above(-1)
done()
})
})
})
it('should only store the values passed in the whitelist', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data, ['username']).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).to.equal(data.username)
expect(_user.secretValue).not.to.equal(data.secretValue)
expect(_user.secretValue).to.equal(null)
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).to.equal(data.username)
expect(_user.secretValue).to.equal(data.secretValue)
done()
})
})
})
it('can omitt autoincremental columns', function(done) {
var self = this
, data = { title: 'Iliad' }
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
, chain = new Sequelize.Utils.QueryChainer()
, chain2 = new Sequelize.Utils.QueryChainer()
, books = []
dataTypes.forEach(function(dataType, index) {
books[index] = self.sequelize.define('Book'+index, {
id: { type: dataType, primaryKey: true, autoIncrement: true },
title: Sequelize.TEXT
})
})
books.forEach(function(b) {
chain.add(b.sync({ force: true }))
})
chain.run().success(function() {
books.forEach(function(b) {
chain2.add(b.create(data))
})
chain2.run().success(function(results) {
results.forEach(function(book, index) {
expect(book.title).to.equal(data.title)
expect(book.author).to.equal(data.author)
expect(books[index].rawAttributes.id.type.toString())
.to.equal(dataTypes[index].toString())
})
done()
})
})
})
it('saves data with single quote', function(done) {
var quote = "single'quote"
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).to.equal(quote)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var quote = 'double"quote'
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).to.equal(quote)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var json = JSON.stringify({ key: 'value' })
, self = this
this.User.create({ data: json }).success(function(user) {
expect(user.data).to.equal(json)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
this.User.create({ username: 'foo' }).success(function(user) {
expect(parseInt(+user.createdAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1.5)
done()
})
})
it('allows setting custom IDs', function(done) {
var self = this
this.User.create({ id: 42 }).success(function (user) {
expect(user.id).to.equal(42)
self.User.find(42).success(function (user) {
expect(user).to.exist
done()
})
})
})
describe('enums', function() {
it('correctly restores enum values', function(done) {
var self = this
, Item = self.sequelize.define('Item', {
state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }
})
Item.sync({ force: true }).success(function() {
Item.create({ state: 'available' }).success(function(_item) {
Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.id).to.equal(_item.id)
done()
})
})
})
})
})
})
describe('bulkCreate', function() {
it('properly handles disparate field lists', function(done) {
var self = this
, data = [{username: 'Peter', secretValue: '42' },
{username: 'Paul'},
{username: 'Steve'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({where: {username: 'Paul'}}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("Paul")
expect(users[0].secretValue).to.be.null
done()
})
})
})
it('inserts multiple values respecting the white list', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data, ['username']).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].secretValue).to.be.null;
expect(users[1].username).to.equal("Paul")
expect(users[1].secretValue).to.be.null;
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].secretValue).to.equal('42')
expect(users[1].username).to.equal("Paul")
expect(users[1].secretValue).to.equal('23')
done()
})
})
})
it('saves data with single quote', function(done) {
var self = this
, quote = "Single'Quote"
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(quote)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var self = this
, quote = 'Double"Quote'
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(quote)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var self = this
, json = JSON.stringify({ key: 'value' })
, data = [{ username: 'Peter', data: json},
{ username: 'Paul', data: json}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(json)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
var self = this
, data = [{ username: 'Peter'},
{ username: 'Paul'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(parseInt(+users[0].createdAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
expect(users[1].username).to.equal("Paul")
expect(parseInt(+users[1].createdAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
done()
})
})
})
it('emits an error when validate is set to true', function(done) {
var Tasks = this.sequelize.define('Task', {
name: {
type: Sequelize.STRING,
validate: {
notNull: { args: true, msg: 'name cannot be null' }
}
},
code: {
type: Sequelize.STRING,
validate: {
len: [3, 10]
}
}
})
Tasks.sync({ force: true }).success(function() {
Tasks.bulkCreate([
{name: 'foo', code: '123'},
{code: '1234'},
{name: 'bar', code: '1'}
], null, {validate: true}).error(function(errors) {
expect(errors).to.not.be.null
expect(errors).to.be.instanceof(Array)
expect(errors).to.have.length(2)
expect(errors[0].record.code).to.equal('1234')
expect(errors[0].errors.name[0]).to.equal('name cannot be null')
expect(errors[1].record.name).to.equal('bar')
expect(errors[1].record.code).to.equal('1')
expect(errors[1].errors.code[0]).to.equal('String is not in range: code')
done()
})
})
})
it("doesn't emit an error when validate is set to true but our selectedValues are fine", function(done) {
var Tasks = this.sequelize.define('Task', {
name: {
type: Sequelize.STRING,
validate: {
notNull: { args: true, msg: 'name cannot be null' }
}
},
code: {
type: Sequelize.STRING,
validate: {
len: [3, 10]
}
}
})
Tasks.sync({ force: true }).success(function() {
Tasks.bulkCreate([
{name: 'foo', code: '123'},
{code: '1234'}
], ['code'], {validate: true}).success(function() {
// we passed!
done()
})
})
})
describe('enums', function() {
it('correctly restores enum values', function(done) {
var self = this
, Item = self.sequelize.define('Item', {
state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] },
name: Sequelize.STRING
})
Item.sync({ force: true }).success(function() {
Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() {
Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.name).to.equal('B')
done()
})
})
})
})
})
})
describe('update', function() {
it('allows sql logging of updated statements', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
paranoid:true
})
User.sync({ force: true }).success(function() {
User.create({ name: 'meg', bio: 'none' }).success(function(u) {
expect(u).to.exist
expect(u).not.to.be.null
u.updateAttributes({name: 'brian'}).on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("UPDATE")).to.be.above(-1)
done()
})
})
})
})
it('updates only values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
users.forEach(function (user) {
if (user.secretValue == '42') {
expect(user.username).to.equal("Bill")
} else {
expect(user.username).to.equal("Bob")
}
})
done()
})
})
})
})
it('sets updatedAt to the current timestamp', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'}).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
expect(users[0].username).to.equal("Bill")
expect(users[1].username).to.equal("Bill")
expect(users[2].username).to.equal("Bob")
expect(parseInt(+users[0].updatedAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1)
expect(parseInt(+users[1].updatedAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1)
done()
})
})
})
})
})
describe('destroy', function() {
it('deletes a record from the database if dao is not paranoid', function(done) {
var UserDestroy = this.sequelize.define('UserDestory', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
UserDestroy.sync({ force: true }).success(function() {
UserDestroy.create({name: 'hallo', bio: 'welt'}).success(function(u) {
UserDestroy.all().success(function(users) {
expect(users.length).to.equal(1)
u.destroy().success(function() {
UserDestroy.all().success(function(users) {
expect(users.length).to.equal(0)
done()
})
})
})
})
})
})
it('allows sql logging of delete statements', function(done) {
var UserDelete = this.sequelize.define('UserDelete', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
UserDelete.sync({ force: true }).success(function() {
UserDelete.create({name: 'hallo', bio: 'welt'}).success(function(u) {
UserDelete.all().success(function(users) {
expect(users.length).to.equal(1)
u.destroy().on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("DELETE")).to.be.above(-1)
done()
})
})
})
})
})
it('deletes values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.destroy({secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("Bob")
done()
})
})
})
})
it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this
, ParanoidUser = self.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, {
paranoid: true
})
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
ParanoidUser.sync({ force: true }).success(function() {
ParanoidUser.bulkCreate(data).success(function() {
var date = parseInt(+new Date()/5000, 10)
ParanoidUser.destroy({secretValue: '42'}).success(function() {
ParanoidUser.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
expect(users[0].username).to.equal("Peter")
expect(users[1].username).to.equal("Paul")
expect(users[2].username).to.equal("Bob")
expect(parseInt(+users[0].deletedAt/5000, 10)).to.equal(date)
expect(parseInt(+users[1].deletedAt/5000, 10)).to.equal(date)
done()
})
})
})
})
})
})
describe('special where conditions/smartWhere object', function() {
beforeEach(function(done) {
var self = this
this.User.bulkCreate([
{username: 'boo', intVal: 5, theDate: '2013-01-01 12:00'},
{username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00'}
]).success(function(user2){
done()
})
})
it('should be able to retun a record with primaryKey being null for new inserts', function(done) {
var Session = this.sequelize.define('Session', {
token: { type: DataTypes.TEXT, allowNull: false },
lastUpdate: { type: DataTypes.DATE, allowNull: false }
}, {
charset: 'utf8',
collate: 'utf8_general_ci',
omitNull: true
})
, User = this.sequelize.define('User', {
name: { type: DataTypes.STRING, allowNull: false, unique: true },
password: { type: DataTypes.STRING, allowNull: false },
isAdmin: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
}, {
charset: 'utf8',
collate: 'utf8_general_ci'
})
User.hasMany(Session, { as: 'Sessions' })
Session.belongsTo(User)
Session.sync({ force: true }).success(function() {
User.sync({ force: true }).success(function() {
User.create({name: 'Name1', password: '123', isAdmin: false}).success(function(user) {
var sess = Session.build({
lastUpdate: new Date(),
token: '123'
})
user.addSession(sess).success(function(u) {
expect(u.token).to.equal('123')
done()
})
})
})
})
})
it('should be able to find a row between a certain date', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row between a certain date and an additional where clause', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
},
intVal: 10
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row not between a certain integer', function(done) {
this.User.findAll({
where: {
intVal: {
nbetween: [8, 10]
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not between and between logic', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2012-12-10', '2013-01-02'],
nbetween: ['2013-01-04', '2013-01-20']
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not between and between logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
between: [new Date('2012-12-10'), new Date('2013-01-02')],
nbetween: [new Date('2013-01-04'), new Date('2013-01-20')]
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using greater than or equal to logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
gte: new Date('2013-01-09')
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row using greater than or equal to', function(done) {
this.User.find({
where: {
intVal: {
gte: 6
}
}
}).success(function(user) {
expect(user.username).to.equal('boo2')
expect(user.intVal).to.equal(10)
done()
})
})
it('should be able to find a row using greater than', function(done) {
this.User.find({
where: {
intVal: {
gt: 5
}
}
}).success(function(user) {
expect(user.username).to.equal('boo2')
expect(user.intVal).to.equal(10)
done()
})
})
it('should be able to find a row using lesser than or equal to', function(done) {
this.User.find({
where: {
intVal: {
lte: 5
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should be able to find a row using lesser than', function(done) {
this.User.find({
where: {
intVal: {
lt: 6
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should have no problem finding a row using lesser and greater than', function(done) {
this.User.findAll({
where: {
intVal: {
lt: 6,
gt: 4
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not equal to logic', function(done) {
this.User.find({
where: {
intVal: {
ne: 10
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should be able to find multiple users with any of the special where logic properties', function(done) {
this.User.findAll({
where: {
intVal: {
lte: 10
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
expect(users[1].username).to.equal('boo2')
expect(users[1].intVal).to.equal(10)
done()
})
})
})
describe('find', function() {
describe('general / basic function', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.UserPrimary = self.sequelize.define('UserPrimary', {
specialKey: {
type: DataTypes.STRING,
primaryKey: true
}
})
self.UserPrimary.sync({force: true}).success(function() {
self.UserPrimary.create({specialKey: 'a string'}).success(function() {
self.user = user
done()
})
})
})
})
it('doesn\'t throw an error when entering in a non integer value for a specified primary field', function(done) {
this.UserPrimary.find('a string').success(function(user) {
expect(user.specialKey).to.equal('a string')
done()
})
})
it('doesn\'t throw an error when entering in a non integer value', function(done) {
this.User.find('a string value').success(function(user) {
expect(user).to.be.null
done()
})
})
it('returns a single dao', function(done) {
var self = this
this.User.find(this.user.id).success(function(user) {
expect(Array.isArray(user)).to.not.be.ok
expect(user.id).to.equal(self.user.id)
expect(user.id).to.equal(1)
done()
})
})
it('returns a single dao given a string id', function(done) {
var self = this
this.User.find(this.user.id + '').success(function(user) {
expect(Array.isArray(user)).to.not.be.ok
expect(user.id).to.equal(self.user.id)
expect(user.id).to.equal(1)
done()
})
})
it("should make aliased attributes available", function(done) {
this.User.find({
where: { id: 1 },
attributes: ['id', ['username', 'name']]
}).success(function(user) {
expect(user.name).to.equal('barfooz')
done()
})
})
it("should not try to convert boolean values if they are not selected", function(done) {
var UserWithBoolean = this.sequelize.define('UserBoolean', {
active: Sequelize.BOOLEAN
})
UserWithBoolean.sync({force: true}).success(function () {
UserWithBoolean.create({ active: true }).success(function(user) {
UserWithBoolean.find({ where: { id: user.id }, attributes: [ 'id' ] }).success(function(user) {
expect(user.active).not.to.exist
done()
})
})
})
})
it('finds a specific user via where option', function(done) {
this.User.find({ where: { username: 'barfooz' } }).success(function(user) {
expect(user.username).to.equal('barfooz')
done()
})
})
it("doesn't find a user if conditions are not matching", function(done) {
this.User.find({ where: { username: 'foo' } }).success(function(user) {
expect(user).to.be.null
done()
})
})
it('allows sql logging', function(done) {
this.User.find({ where: { username: 'foo' } }).on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it('ignores passed limit option', function(done) {
this.User.find({ limit: 10 }).success(function(user) {
// it returns an object instead of an array
expect(Array.isArray(user)).to.not.be.ok
expect(user.hasOwnProperty('username')).to.be.ok
done()
})
})
it('finds entries via primary keys', function(done) {
var self = this
, UserPrimary = self.sequelize.define('UserWithPrimaryKey', {
identifier: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING
})
UserPrimary.sync({ force: true }).success(function() {
UserPrimary.create({
identifier: 'an identifier',
name: 'John'
}).success(function(u) {
expect(u.id).not.to.exist
UserPrimary.find('an identifier').success(function(u2) {
expect(u2.identifier).to.equal('an identifier')
expect(u2.name).to.equal('John')
done()
})
})
})
})
it('returns the selected fields as instance.selectedValues', function(done) {
var self = this
this.User.create({
username: 'JohnXOXOXO'
}).success(function() {
self.User.find({
where: { username: 'JohnXOXOXO' },
attributes: ['username']
}).success(function(user) {
expect(user.selectedValues).to.have.property('username', 'JohnXOXOXO')
done()
})
})
})
it('returns the selected fields and all fields of the included table as instance.selectedValues', function(done) {
var self = this
self.Mission = self.sequelize.define('Mission', {
title: {type: Sequelize.STRING, defaultValue: 'a mission!!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
})
self.Mission.belongsTo(self.User)
self.User.hasMany(self.Mission)
self.Mission.sync({ force: true }).success(function() {
self.Mission.create().success(function(mission) {
self.User.create({username: 'John DOE'}).success(function(user) {
mission.setUser(user).success(function() {
self.User.find({
where: { username: 'John DOE' },
attributes: ['username'],
include: [self.Mission]
}).success(function(user) {
expect(user.selectedValues).to.deep.equal({ username: 'John DOE' })
done()
})
})
})
})
})
})
it('always honors ZERO as primary key', function(_done) {
var self = this
, permutations = [
0,
'0',
{where: {id: 0}},
{where: {id: '0'}}
]
, done = _.after(2 * permutations.length, _done)
this.User.bulkCreate([{username: 'jack'}, {username: 'jack'}]).success(function() {
permutations.forEach(function(perm) {
self.User.find(perm).done(function(err, user) {
expect(err).to.be.null
expect(user).to.be.null
done()
}).on('sql', function(s) {
expect(s.indexOf(0)).not.to.equal(-1)
done()
})
})
})
})
it('should allow us to find IDs using capital letters', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
ID: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
Login: { type: Sequelize.STRING }
})
User.sync({ force: true }).success(function() {
User.create({Login: 'foo'}).success(function() {
User.find(1).success(function(user) {
expect(user).to.exist
expect(user.ID).to.equal(1)
done()
})
})
})
})
})
describe('eager loading', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
this.init = function(callback) {
self.Task.sync({ force: true }).success(function() {
self.Worker.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
callback()
})
})
})
})
}
done()
})
describe('belongsTo', function() {
describe('generic', function() {
it('throws an error about unexpected input if include contains a non-object', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ 1 ] })
}).to.throw(Error, 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
done()
})
it('throws an error about missing attributes if include contains an object with daoFactory', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Worker } ] })
}).to.throw(Error, 'Include malformed. Expected attributes: daoFactory, as!')
done()
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('returns the associated worker via task.worker', function(done) {
var self = this
this.Task.belongsTo(this.Worker)
this.init(function() {
self.task.setWorker(self.worker).success(function() {
self.Task.find({
where: { title: 'homework' },
include: [ self.Worker ]
}).complete(function(err, task) {
expect(err).to.be.null
expect(task).to.exist
expect(task.worker).to.exist
expect(task.worker.name).to.equal('worker')
done()
})
})
})
})
})
it('returns the private and public ip', function(done) {
var self = Object.create(this)
self.Domain = self.sequelize.define('Domain', { ip: Sequelize.STRING })
self.Environment = self.sequelize.define('Environment', { name: Sequelize.STRING })
self.Environment
.belongsTo(self.Domain, { as: 'PrivateDomain', foreignKey: 'privateDomainId' })
.belongsTo(self.Domain, { as: 'PublicDomain', foreignKey: 'publicDomainId' })
self.Domain.sync({ force: true }).success(function() {
self.Environment.sync({ force: true }).success(function() {
self.Domain.create({ ip: '192.168.0.1' }).success(function(privateIp) {
self.Domain.create({ ip: '91.65.189.19' }).success(function(publicIp) {
self.Environment.create({ name: 'environment' }).success(function(env) {
env.setPrivateDomain(privateIp).success(function() {
env.setPublicDomain(publicIp).success(function() {
self.Environment.find({
where: { name: 'environment' },
include: [
{ daoFactory: self.Domain, as: 'PrivateDomain' },
{ daoFactory: self.Domain, as: 'PublicDomain' }
]
}).complete(function(err, environment) {
expect(err).to.be.null
expect(environment).to.exist
expect(environment.privateDomain).to.exist
expect(environment.privateDomain.ip).to.equal('192.168.0.1')
expect(environment.publicDomain).to.exist
expect(environment.publicDomain.ip).to.equal('91.65.189.19')
done()
})
})
})
})
})
})
})
})
})
})
describe('hasOne', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasOne(this.Task)
this.init(function() {
self.worker.setTask(self.task).success(function() {
done()
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.find({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.task).to.exist
expect(worker.task.title).to.equal('homework')
done()
})
})
})
describe('hasOne with alias', function() {
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
describe('alias', function(done) {
beforeEach(function(done) {
var self = this
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.init(function() {
self.worker.setToDo(self.task).success(function() {
done()
})
})
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.toDo).to.exist
expect(worker.toDo.title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(worker.toDo.title).to.equal('homework')
done()
})
})
})
})
describe('hasMany', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasMany(this.Task)
this.init(function() {
self.worker.setTasks([ self.task ]).success(function() {
done()
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.find({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.tasks).to.exist
expect(worker.tasks[0].title).to.equal('homework')
done()
})
})
})
describe('hasMany with alias', function() {
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
describe('alias', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.init(function() {
self.worker.setToDos([ self.task ]).success(function() {
done()
})
})
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.toDos).to.exist
expect(worker.toDos[0].title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(worker.toDos[0].title).to.equal('homework')
done()
})
})
})
})
})
describe('queryOptions', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.user = user
done()
})
})
it("should return a DAO when queryOptions are not set", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}).done(function(err, user) {
expect(user).to.be.instanceOf(self.User.DAO)
done()
})
})
it("should return a DAO when raw is false", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}, { raw: false }).done(function(err, user) {
expect(user).to.be.instanceOf(self.User.DAO)
done()
})
})
it("should return raw data when raw is true", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}, { raw: true }).done(function(err, user) {
expect(user).to.not.be.instanceOf(self.User.DAO)
expect(user).to.be.instanceOf(Object)
done()
})
})
})
})
describe('findAll', function() {
describe('eager loading', function() {
describe('belongsTo', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('TaskBelongsTo', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Task.belongsTo(self.Worker)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.task.setWorker(self.worker).success(function() {
done()
})
})
})
})
})
})
it('throws an error about unexpected input if include contains a non-object', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ 1 ] })
}).to.throw(Error, 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
done()
})
it('throws an error about missing attributes if include contains an object with daoFactory', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ { daoFactory: self.Worker } ] })
}).to.throw(Error, 'Include malformed. Expected attributes: daoFactory, as!')
done()
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ self.Task ] })
}).to.throw(Error, 'TaskBelongsTo is not associated to Worker!')
done()
})
it('returns the associated worker via task.worker', function(done) {
this.Task.all({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, tasks) {
expect(err).to.be.null
expect(tasks).to.exist
expect(tasks[0].worker).to.exist
expect(tasks[0].worker.name).to.equal('worker')
done()
})
})
})
describe('hasOne', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('TaskHasOne', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasOne(self.Task)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setTaskHasOne(self.task).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.all({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to TaskHasOne!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].taskHasOne).to.exist
expect(workers[0].taskHasOne.title).to.equal('homework')
done()
})
})
})
describe('hasOne with alias', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasOne(self.Task, { as: 'ToDo' })
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setToDo(self.task).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].toDo).to.exist
expect(workers[0].toDo.title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(workers[0].toDo.title).to.equal('homework')
done()
})
})
})
describe('hasMany', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasMany(self.Task)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setTasks([ self.task ]).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.findAll({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].tasks).to.exist
expect(workers[0].tasks[0].title).to.equal('homework')
done()
})
})
})
describe('hasMany with alias', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasMany(self.Task, { as: 'ToDos' })
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setToDos([ self.task ]).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.findAll({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.findAll({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].toDos).to.exist
expect(workers[0].toDos[0].title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(workers[0].toDos[0].title).to.equal('homework')
done()
})
})
})
describe('queryOptions', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.user = user
done()
})
})
it("should return a DAO when queryOptions are not set", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}).done(function(err, users) {
users.forEach(function (user) {
expect(user).to.be.instanceOf(self.User.DAO)
})
done()
})
})
it("should return a DAO when raw is false", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}, { raw: false }).done(function(err, users) {
users.forEach(function (user) {
expect(user).to.be.instanceOf(self.User.DAO)
})
done()
})
})
it("should return raw data when raw is true", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}, { raw: true }).done(function(err, users) {
users.forEach(function(user) {
expect(user).to.not.be.instanceOf(self.User.DAO)
expect(users[0]).to.be.instanceOf(Object)
})
done()
})
})
})
})
describe('normal findAll', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'user', data: 'foobar', theDate: moment().toDate()}).success(function(user) {
self.User.create({username: 'user2', data: 'bar', theDate: moment().toDate()}).success(function(user2){
self.users = [user].concat(user2)
done()
})
})
})
it("finds all entries", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).to.equal(2)
done()
})
})
it("finds all users matching the passed conditions", function(done) {
this.User.findAll({where: "id != " + this.users[1].id}).success(function(users) {
expect(users.length).to.equal(1)
done()
})
})
it("can also handle array notation", function(done) {
var self = this
this.User.findAll({where: ['id = ?', this.users[1].id]}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].id).to.equal(self.users[1].id)
done()
})
})
it("sorts the results via id in ascending order", function(done) {
this.User.findAll().success(function(users) {
expect(users.length).to.equal(2);
expect(users[0].id).to.be.below(users[1].id)
done()
})
})
it("sorts the results via id in descending order", function(done) {
this.User.findAll({ order: "id DESC" }).success(function(users) {
expect(users[0].id).to.be.above(users[1].id)
done()
})
})
it("sorts the results via a date column", function(done) {
var self = this
self.User.create({username: 'user3', data: 'bar', theDate: moment().add('hours', 2).toDate()}).success(function(){
self.User.findAll({ order: 'theDate DESC' }).success(function(users) {
expect(users[0].id).to.be.above(users[2].id)
done()
})
})
})
it("handles offset and limit", function(done) {
var self = this
this.User.bulkCreate([{username: 'bobby'}, {username: 'tables'}]).success(function() {
self.User.findAll({ limit: 2, offset: 2 }).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].id).to.equal(3)
done()
})
})
})
it('should allow us to find IDs using capital letters', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
ID: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
Login: { type: Sequelize.STRING }
})
User.sync({ force: true }).success(function() {
User.create({Login: 'foo'}).success(function() {
User.findAll({ID: 1}).success(function(user) {
expect(user).to.be.instanceof(Array)
expect(user).to.have.length(1)
done()
})
})
})
})
})
})
describe('findAndCountAll', function() {
beforeEach(function(done) {
var self = this
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'},
{username: 'bobby', data: 'foo'}
]).success(function() {
self.User.all().success(function(users){
self.users = users
done()
})
})
})
it("handles where clause [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles where clause with ordering [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, order: 'id ASC'}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles offset", function(done) {
this.User.findAndCountAll({offset: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles limit", function(done) {
this.User.findAndCountAll({limit: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(1)
done()
})
})
it("handles offset and limit", function(done) {
this.User.findAndCountAll({offset: 1, limit: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(1)
done()
})
})
})
describe('all', function() {
beforeEach(function(done) {
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'}
]).complete(function() {
done()
})
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).to.equal(2)
done()
})
})
})
describe('equals', function() {
it("correctly determines equality of objects", function(done) {
this.User.create({username: 'hallo', data: 'welt'}).success(function(u) {
expect(u.equals(u)).to.be.ok
done()
})
})
// sqlite can't handle multiple primary keys
if(dialect !== "sqlite") {
it("correctly determines equality with multiple primary keys", function(done) {
var userKeys = this.sequelize.define('userkeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
userKeys.sync({ force: true }).success(function() {
userKeys.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equals(u)).to.be.ok
done()
})
})
})
}
})
describe('equalsOneOf', function() {
// sqlite can't handle multiple primary keys
if (dialect !== "sqlite") {
beforeEach(function(done) {
this.userKey = this.sequelize.define('userKeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.userKey.sync({ force: true }).success(function(){
done()
})
})
it('determines equality if one is matching', function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([u, {a: 1}])).to.be.ok
done()
})
})
it("doesn't determine equality if none is matching", function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([{b: 2}, {a: 1}])).to.not.be.ok
done()
})
})
}
})
describe('count', function() {
it('counts all created objects', function(done) {
var self = this
this.User.bulkCreate([{username: 'user1'}, {username: 'user2'}]).success(function() {
self.User.count().success(function(count) {
expect(count).to.equal(2)
done()
})
})
})
it('allows sql logging', function(done) {
this.User.count().on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it('filters object', function(done) {
var self = this
this.User.create({username: 'user1'}).success(function() {
self.User.create({username: 'foo'}).success(function() {
self.User.count({where: "username LIKE '%us%'"}).success(function(count) {
expect(count).to.equal(1)
done()
})
})
})
})
})
describe('min', function() {
beforeEach(function(done) {
var self = this
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function() {
self.UserWithDec.sync({ force: true }).success(function() {
done()
})
})
})
it("should return the min value", function(done) {
var self = this
this.UserWithAge.bulkCreate([{age: 3}, { age: 2 }]).success(function() {
self.UserWithAge.min('age').success(function(min) {
expect(min).to.equal(2)
done()
})
})
})
it('allows sql logging', function(done) {
this.UserWithAge.min('age').on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it("should allow decimals in min", function(done){
var self = this
this.UserWithDec.bulkCreate([{value: 5.5}, {value: 3.5}]).success(function(){
self.UserWithDec.min('value').success(function(min){
expect(min).to.equal(3.5)
done()
})
})
})
})
describe('max', function() {
beforeEach(function(done) {
var self = this
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER,
order: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function() {
self.UserWithDec.sync({ force: true }).success(function() {
done()
})
})
})
it("should return the max value for a field named the same as an SQL reserved keyword", function(done) {
var self = this
this.UserWithAge.bulkCreate([{age: 2, order: 3}, {age: 3, order: 5}]).success(function(){
self.UserWithAge.max('order').success(function(max) {
expect(max).to.equal(5)
done()
})
})
})
it("should return the max value", function(done) {
var self = this
self.UserWithAge.bulkCreate([{age: 2}, {age: 3}]).success(function() {
self.UserWithAge.max('age').success(function(max) {
expect(max).to.equal(3)
done()
})
})
})
it("should allow decimals in max", function(done) {
var self = this
this.UserWithDec.bulkCreate([{value: 3.5}, {value: 5.5}]).success(function(){
self.UserWithDec.max('value').success(function(max){
expect(max).to.equal(5.5)
done()
})
})
})
it('allows sql logging', function(done) {
this.UserWithAge.max('age').on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
})
describe('schematic support', function() {
beforeEach(function(done){
var self = this;
this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER
})
this.UserSpecial = this.sequelize.define('UserSpecial', {
age: Sequelize.INTEGER
})
self.sequelize.dropAllSchemas().success(function(){
self.sequelize.createSchema('schema_test').success(function(){
self.sequelize.createSchema('special').success(function(){
self.UserSpecial.schema('special').sync({force: true}).success(function(UserSpecialSync) {
self.UserSpecialSync = UserSpecialSync
done()
})
})
})
})
})
it("should be able to list schemas", function(done){
this.sequelize.showAllSchemas().success(function(schemas) {
expect(schemas).to.exist
expect(schemas[0]).to.be.instanceof(Array)
// sqlite & MySQL doesn't actually create schemas unless Model.sync() is called
// Postgres supports schemas natively
expect(schemas[0]).to.have.length((dialect === "postgres" || dialect === "postgres-native" ? 2 : 1))
done()
})
})
if (dialect === "mysql") {
it("should take schemaDelimiter into account if applicable", function(done){
var UserSpecialUnderscore = this.sequelize.define('UserSpecialUnderscore', {age: Sequelize.INTEGER}, {schema: 'hello', schemaDelimiter: '_'})
var UserSpecialDblUnderscore = this.sequelize.define('UserSpecialDblUnderscore', {age: Sequelize.INTEGER})
UserSpecialUnderscore.sync({force: true}).success(function(User){
UserSpecialDblUnderscore.schema('hello', '__').sync({force: true}).success(function(DblUser){
DblUser.create({age: 3}).on('sql', function(dblSql){
User.create({age: 3}).on('sql', function(sql){
expect(dblSql).to.exist
expect(dblSql.indexOf('INSERT INTO `hello__UserSpecialDblUnderscores`')).to.be.above(-1)
expect(sql).to.exist
expect(sql.indexOf('INSERT INTO `hello_UserSpecialUnderscores`')).to.be.above(-1)
done()
})
})
})
})
})
}
it("should be able to create and update records under any valid schematic", function(done){
var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){
expect(UserSpecial).to.exist
expect(UserPublic).to.exist
if (dialect === "postgres") {
expect(self.UserSpecialSync.getTableName()).to.equal('"special"."UserSpecials"');
expect(UserSpecial.indexOf('INSERT INTO "special"."UserSpecials"')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO "UserPublics"')).to.be.above(-1)
} else if (dialect === "sqlite") {
expect(self.UserSpecialSync.getTableName()).to.equal('`special`.`UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
} else {
expect(self.UserSpecialSync.getTableName()).to.equal('`special.UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
}
})
.success(function(UserSpecial){
UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){
expect(user).to.exist
if (dialect === "postgres") {
expect(user.indexOf('UPDATE "special"."UserSpecials"')).to.be.above(-1)
} else {
expect(user.indexOf('UPDATE `special.UserSpecials`')).to.be.above(-1)
}
done()
})
})
})
})
})
})
describe('references', function() {
this.timeout(3000)
beforeEach(function(done) {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING })
this.Author.sync({ force: true }).success(function() {
done()
})
})
afterEach(function(done) {
var self = this
self.sequelize.getQueryInterface().dropTable('posts', { force: true }).success(function() {
self.sequelize.getQueryInterface().dropTable('authors', { force: true }).success(function() {
done()
})
})
})
it('uses an existing dao factory and references the author table', function(done) {
var self = this
, Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: this.Author,
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
}
else if (dialect === 'mysql') {
expect(sql).to.match(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
}
else if (dialect === 'sqlite') {
expect(sql).to.match(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
it('uses a table name as a string and references the author table', function(done) {
var self = this
, Post = self.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: 'authors',
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
}
else if (dialect === 'mysql') {
expect(sql).to.match(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
}
else if (dialect === 'sqlite') {
expect(sql).to.match(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
it("emits an error event as the referenced table name is invalid", function(done) {
var self = this
, Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: '4uth0r5',
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().success(function() {
if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).to.equal(1)
done()
} else {
// the parser should not end up here ...
expect(2).to.equal(1)
done()
}
}).error(function(err) {
if (dialect === 'mysql') {
expect(err.message).to.match(/ER_CANNOT_ADD_FOREIGN|ER_CANT_CREATE_TABLE/)
}
else if (dialect === 'sqlite') {
// the parser should not end up here ... see above
expect(1).to.equal(2)
}
else if (dialect === 'postgres') {
expect(err.message).to.match(/relation "4uth0r5" does not exist/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
})
describe("syntax sugar", function() {
before(function(done) {
this.User = this.sequelize.define("user", {
username: Sequelize.STRING,
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe("dataset", function() {
it("returns a node-sql instance with the correct dialect", function() {
expect(this.User.dataset().sql.dialectName).to.equal(dialect)
})
it("allows me to generate sql queries", function() {
var query = this.User.dataset().select("username").toQuery()
expect(Object.keys(query)).to.eql(['text', 'values'])
})
})
describe("select", function() {
it("sets .select() as an alias to .dataset().select()", function() {
var query1 = this.User.select("username").toQuery()
, query2 = this.User.dataset().select("username").toQuery()
expect(query1.text).to.equal(query2.text)
})
})
describe("toSql", function() {
it("transforms the node-sql instance into a proper sql string", function() {
var sql = this.User.select("username").toSql()
var sqlMap = {
postgres: 'SELECT username FROM "' + this.User.tableName + '";',
mysql: 'SELECT username FROM `' + this.User.tableName + '`;',
sqlite: 'SELECT username FROM "' + this.User.tableName + '";'
}
expect(sql).to.equal(sqlMap[dialect])
})
it("transforms node-sql instances with chaining into a proper sql string", function() {
var sql = this.User.select("username").select("firstName").group("username").toSql()
var sqlMap = {
postgres: 'SELECT username, firstName FROM "' + this.User.tableName + '" GROUP BY username;',
mysql: 'SELECT username, firstName FROM `' + this.User.tableName + '` GROUP BY username;',
sqlite: 'SELECT username, firstName FROM "' + this.User.tableName + '" GROUP BY username;'
}
expect(sql).to.equal(sqlMap[dialect])
})
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAO"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
aDate: { type: DataTypes.DATE },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1,20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe('Escaping', function() {
it('is done properly for special characters', function(done) {
// Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a"
// But this causes sqlite to fail and exits the entire test suite immediately
var bio = dialect + "'\"\n" // Need to add the dialect here so in case of failure I know what DB it failed for
, self = this
this.User.create({ username: bio }).success(function(u1) {
self.User.find(u1.id).success(function(u2) {
expect(u2.username).to.equal(bio)
done()
})
})
})
})
describe('isNewRecord', function() {
it('returns true for non-saved objects', function(done) {
var user = this.User.build({ username: 'user' })
expect(user.id).to.be.null
expect(user.isNewRecord).to.be.ok
done()
})
it("returns false for saved objects", function(done) {
this.User.build({ username: 'user' }).save().success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
it("returns false for created objects", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
it("returns false for objects found by find method", function(done) {
var self = this
this.User.create({ username: 'user' }).success(function() {
self.User.create({ username: 'user' }).success(function(user) {
self.User.find(user.id).success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
})
})
it("returns false for objects found by findAll method", function(done) {
var self = this
, users = []
for (var i = 0; i < 10; i++) {
users[users.length] = {username: 'user'}
}
this.User.bulkCreate(users).success(function() {
self.User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isNewRecord).to.not.be.ok
})
done()
})
})
})
})
describe('isDirty', function() {
it('returns true for non-saved objects', function(done) {
var user = this.User.build({ username: 'user' })
expect(user.id).to.be.null
expect(user.isDirty).to.be.true
done()
})
it("returns false for saved objects", function(done) {
this.User.build({ username: 'user' }).save().success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'new'
expect(user.isDirty).to.be.true
done()
})
})
it("returns false for non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'user'
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for non-changed date attribute", function(done) {
this.User.create({ aDate: new Date(2013, 6, 31, 14, 25, 21) }).success(function(user) {
user.aDate = '2013-07-31 14:25:21'
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for two empty attributes", function(done) {
this.User.create({ username: null }).success(function(user) {
user.username = ''
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for bulk changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.setAttributes({
username: 'new',
aNumber: 1
})
expect(user.isDirty).to.be.true
done()
})
})
it("returns false for bulk non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.setAttributes({
username: 'user'
})
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for changed and bulk non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.aNumber = 23
user.setAttributes({
username: 'user'
})
expect(user.isDirty).to.be.true
done()
})
})
it("returns true for changed attribute and false for saved object", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'new'
expect(user.isDirty).to.be.true
user.save().success(function() {
expect(user.isDirty).to.be.false
done()
})
})
})
it("returns false for created objects", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for objects found by find method", function(done) {
var self = this
this.User.create({ username: 'user' }).success(function(user) {
self.User.find(user.id).success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
})
it("returns false for objects found by findAll method", function(done) {
var self = this
, users = []
for (var i = 0; i < 10; i++) {
users[users.length] = {username: 'user'}
}
this.User.bulkCreate(users).success(function() {
self.User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isDirty).to.be.false
})
done()
})
})
})
})
describe('increment', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(function(){
done()
})
})
it('with array', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(2)
done()
})
})
})
})
it('with single field', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment('aNumber', 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(2)
done()
})
})
})
})
it('should still work right with other concurrent updates', function(done) {
var self = this
this.User.find(1).complete(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).complete(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).complete(function () {
user1.increment(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user5) {
expect(user5.aNumber).to.be.equal(3)
done()
})
})
})
})
})
})
it('should still work right with other concurrent increments', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
var _done = _.after(3, function() {
self.User.find(1).complete(function(err, user2) {
expect(user2.aNumber).to.equal(6)
done()
})
})
user1.increment(['aNumber'], 2).complete(_done)
user1.increment(['aNumber'], 2).complete(_done)
user1.increment(['aNumber'], 2).complete(_done)
})
})
it('with key value pair', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment({ 'aNumber': 1, 'bNumber': 2}).success(function() {
self.User.find(1).complete(function (err, user3) {
expect(user3.aNumber).to.be.equal(1)
expect(user3.bNumber).to.be.equal(2)
done()
})
})
})
})
})
describe('decrement', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(done)
})
it('with array', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-2)
done()
})
})
})
})
it('with single field', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement('aNumber', 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-2)
done()
})
})
})
})
it('should still work right with other concurrent updates', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).complete(function(err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).complete(function () {
user1.decrement(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user5) {
expect(user5.aNumber).to.be.equal(-1)
done()
})
})
})
})
})
})
it('should still work right with other concurrent increments', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
var _done = _.after(3, function() {
self.User.find(1).complete(function (err, user2) {
expect(user2.aNumber).to.equal(-6)
done()
})
})
user1.decrement(['aNumber'], 2).complete(_done)
user1.decrement(['aNumber'], 2).complete(_done)
user1.decrement(['aNumber'], 2).complete(_done)
})
})
it('with key value pair', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement({ 'aNumber': 1, 'bNumber': 2}).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-1)
expect(user3.bNumber).to.be.equal(-2)
done()
})
})
})
})
})
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function(done) {
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
originalUser.updateAttributes({ username: 'Doe John' }).complete(function() {
originalUser.reload().complete(function (err, updatedUser) {
expect(originalUser === updatedUser).to.be.true
done()
})
})
})
})
it("should update the values on all references to the DAO", function(done) {
var self = this
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
self.User.find(originalUser.id).complete(function(err, updater) {
updater.updateAttributes({ username: 'Doe John' }).complete(function() {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).to.equal('John Doe')
originalUser.reload().complete(function(err, updatedUser) {
expect(originalUser.username).to.equal('Doe John')
expect(updatedUser.username).to.equal('Doe John')
done()
})
})
})
})
})
it("should update read only attributes as well (updatedAt)", function(done) {
var self = this
this.timeout = 2000
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
var originallyUpdatedAt = originalUser.updatedAt
// Wait for a second, so updatedAt will actually be different
setTimeout(function () {
self.User.find(originalUser.id).complete(function(err, updater) {
updater.updateAttributes({ username: 'Doe John' }).complete(function () {
originalUser.reload().complete(function(err, updatedUser) {
expect(originalUser.updatedAt).to.be.above(originallyUpdatedAt)
expect(updatedUser.updatedAt).to.be.above(originallyUpdatedAt)
done()
})
})
})
}, 1000)
})
})
it("should update the associations as well", function(done) {
var Book = this.sequelize.define('Book', { title: DataTypes.STRING })
, Page = this.sequelize.define('Page', { content: DataTypes.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
Book.sync().success(function() {
Page.sync().success(function() {
Book.create({ title: 'A very old book' }).success(function(book) {
Page.create({ content: 'om nom nom' }).success(function(page) {
book.setPages([ page ]).success(function() {
Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).success(function(leBook) {
page.updateAttributes({ content: 'something totally different' }).success(function(page) {
expect(leBook.pages[0].content).to.equal('om nom nom')
expect(page.content).to.equal('something totally different')
leBook.reload().success(function(leBook) {
expect(leBook.pages[0].content).to.equal('something totally different')
expect(page.content).to.equal('something totally different')
done()
})
})
})
})
})
})
})
})
})
})
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function(done) {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt).to.be.instanceof(Date)
done()
})
it("should store the current date in touchedAt", function(done) {
var clock = sinon.useFakeTimers()
clock.tick(5000)
var user = this.User.build({ username: 'a user'})
clock.restore()
expect(+user.touchedAt).to.be.equal(5000)
done()
})
})
describe('allowNull date', function() {
it('should be just "null" and not Date with Invalid Date', function(done) {
var self = this
this.User.build({ username: 'a user'}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue).to.be.null
done()
})
})
})
it('should be the same valid date when saving the date', function(done) {
var self = this
var date = new Date()
this.User.build({ username: 'a user', dateAllowNullTrue: date}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue.toString()).to.equal(date.toString())
done()
})
})
})
})
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err) {
expect(err).to.exist
expect(err.message).to.exist
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).to.be.null
expect(result).to.exist
done()
})
})
})
describe('save', function() {
this.timeout(3000) // for update timestamp
it('only updates fields in passed array', function(done) {
var self = this
, userId = null
, date = new Date(1990, 01, 01)
this.User.create({
username: 'foo',
touchedAt: new Date()
}).success(function(user) {
user.username = 'fizz'
user.touchedAt = date
user.save(['username']).success(function(){
// re-select user
self.User.find(user.id).success(function(user2) {
// name should have changed
expect(user2.username).to.equal('fizz')
// bio should be unchanged
expect(user2.birthDate).not.to.equal(date)
done()
})
})
})
})
it("stores an entry in the database", function(done) {
var username = 'user'
, User = this.User
, user = this.User.build({
username: username,
touchedAt: new Date(1984, 8, 23)
})
User.all().success(function(users) {
expect(users).to.have.length(0)
user.save().success(function(){
User.all().success(function(users) {
expect(users).to.have.length(1)
expect(users[0].username).to.equal(username)
expect(users[0].touchedAt).to.be.instanceof(Date)
expect(users[0].touchedAt).to.equalDate(new Date(1984, 8, 23))
done()
})
})
})
})
it("updates the timestamps", function(done) {
var now = Date.now()
, user = null
, updatedAt = null
, User = this.User
// timeout is needed, in order to check the update of the timestamp
var build = function(callback) {
user = User.build({ username: 'user' })
updatedAt = user.updatedAt
expect(updatedAt.getTime()).to.be.above(now)
setTimeout(function() {
user.save().success(function() {
expect(updatedAt.getTime()).to.be.below(user.updatedAt.getTime())
callback()
})
}, 1000)
}
// closures are fun :)
setTimeout(function() {
build(function() {
done()
})
}, 1000)
})
describe('without timestamps option', function() {
it("doesn't update the updatedAt column", function(done) {
var User2 = this.sequelize.define('User2', {
username: DataTypes.STRING,
updatedAt: DataTypes.DATE
}, { timestamps: false })
User2.sync().success(function() {
User2.create({ username: 'john doe' }).success(function(johnDoe) {
// sqlite and mysql return undefined, whereas postgres returns null
expect([undefined, null].indexOf(johnDoe.updatedAt)).not.to.be.equal(-1)
done()
})
})
})
})
it('should fail a validation upon creating', function(done){
this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.above(-1)
done()
})
})
it('should fail a validation upon building', function(done){
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateCustom).to.exist
expect(err.validateCustom).to.be.instanceof(Array)
expect(err.validateCustom[0]).to.exist
expect(err.validateCustom[0]).to.equal('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done){
this.User.create({aNumber: 0}).success(function(user){
user.updateAttributes({validateTest: 'hello'}).error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateTest).to.exist
expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer:')).to.be.above(-1)
done()
})
})
})
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).to.equal(0)
done()
})
})
it('saves a record with no primary key', function(done){
var HistoryLog = this.sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
HistoryLog.sync().success(function() {
HistoryLog.create({ someText: 'Some random text', aNumber: 3, aRandomId: 5 }).success(function(log) {
log.updateAttributes({ aNumber: 5 }).success(function(newLog){
expect(newLog.aNumber).to.equal(5)
done()
})
})
})
})
describe('eagerly loaded objects', function() {
beforeEach(function(done) {
var self = this
this.UserEager = this.sequelize.define('UserEagerLoadingSaves', {
username: DataTypes.STRING,
age: DataTypes.INTEGER
}, { timestamps: false })
this.ProjectEager = this.sequelize.define('ProjectEagerLoadingSaves', {
title: DataTypes.STRING,
overdue_days: DataTypes.INTEGER
}, { timestamps: false })
this.UserEager.hasMany(this.ProjectEager, { as: 'Projects' })
this.ProjectEager.belongsTo(this.UserEager, { as: 'Poobah' })
self.UserEager.sync({force: true}).success(function() {
self.ProjectEager.sync({force: true}).success(function() {
done()
})
})
})
it('saves one object that has a collection of eagerly loaded objects', function(done) {
var self = this
this.UserEager.create({ username: 'joe', age: 1 }).success(function(user) {
self.ProjectEager.create({ title: 'project-joe1', overdue_days: 0 }).success(function(project1) {
self.ProjectEager.create({ title: 'project-joe2', overdue_days: 0 }).success(function(project2) {
user.setProjects([project1, project2]).success(function() {
self.UserEager.find({where: {age: 1}, include: [{model: self.ProjectEager, as: 'Projects'}]}).success(function(user) {
expect(user.username).to.equal('joe')
expect(user.age).to.equal(1)
expect(user.projects).to.exist
expect(user.projects.length).to.equal(2)
user.age = user.age + 1 // happy birthday joe
user.save().success(function() {
expect(user.username).to.equal('joe')
expect(user.age).to.equal(2)
expect(user.projects).to.exist
expect(user.projects.length).to.equal(2)
done()
})
})
})
})
})
})
})
it('saves many objects that each a have collection of eagerly loaded objects', function(done) {
var self = this
this.UserEager.create({ username: 'bart', age: 20 }).success(function(bart) {
self.UserEager.create({ username: 'lisa', age: 20 }).success(function(lisa) {
self.ProjectEager.create({ title: 'detention1', overdue_days: 0 }).success(function(detention1) {
self.ProjectEager.create({ title: 'detention2', overdue_days: 0 }).success(function(detention2) {
self.ProjectEager.create({ title: 'exam1', overdue_days: 0 }).success(function(exam1) {
self.ProjectEager.create({ title: 'exam2', overdue_days: 0 }).success(function(exam2) {
bart.setProjects([detention1, detention2]).success(function() {
lisa.setProjects([exam1, exam2]).success(function() {
self.UserEager.findAll({where: {age: 20}, order: 'username ASC', include: [{model: self.ProjectEager, as: 'Projects'}]}).success(function(simpsons) {
var _bart, _lisa
expect(simpsons.length).to.equal(2)
_bart = simpsons[0]
_lisa = simpsons[1]
expect(_bart.projects).to.exist
expect(_lisa.projects).to.exist
expect(_bart.projects.length).to.equal(2)
expect(_lisa.projects.length).to.equal(2)
_bart.age = _bart.age + 1 // happy birthday bart - off to Moe's
_bart.save().success(function(savedbart) {
expect(savedbart.username).to.equal('bart')
expect(savedbart.age).to.equal(21)
_lisa.username = 'lsimpson'
_lisa.save().success(function(savedlisa) {
expect(savedlisa.username).to.equal('lsimpson')
expect(savedlisa.age).to.equal(20)
done()
})
})
})
})
})
})
})
})
})
})
})
})
it('saves many objects that each has one eagerly loaded object (to which they belong)', function(done) {
var self = this
this.UserEager.create({ username: 'poobah', age: 18 }).success(function(user) {
self.ProjectEager.create({ title: 'homework', overdue_days: 10 }).success(function(homework) {
self.ProjectEager.create({ title: 'party', overdue_days: 2 }).success(function(party) {
user.setProjects([homework, party]).success(function() {
self.ProjectEager.findAll({include: [{model: self.UserEager, as: 'Poobah'}]}).success(function(projects) {
expect(projects.length).to.equal(2)
expect(projects[0].poobah).to.exist
expect(projects[1].poobah).to.exist
expect(projects[0].poobah.username).to.equal('poobah')
expect(projects[1].poobah.username).to.equal('poobah')
projects[0].title = 'partymore'
projects[1].title = 'partymore'
projects[0].overdue_days = 0
projects[1].overdue_days = 0
projects[0].save().success(function() {
projects[1].save().success(function() {
self.ProjectEager.findAll({where: {title: 'partymore', overdue_days: 0}, include: [{model: self.UserEager, as: 'Poobah'}]}).success(function(savedprojects) {
expect(savedprojects.length).to.equal(2)
expect(savedprojects[0].poobah).to.exist
expect(savedprojects[1].poobah).to.exist
expect(savedprojects[0].poobah.username).to.equal('poobah')
expect(savedprojects[1].poobah.username).to.equal('poobah')
done()
})
})
})
})
})
})
})
})
})
})
})
describe('toJSON', function() {
beforeEach(function(done) {
var self = this
this.User = this.sequelize.define('UserWithUsernameAndAgeAndIsAdmin', {
username: DataTypes.STRING,
age: DataTypes.INTEGER,
isAdmin: DataTypes.BOOLEAN
}, { timestamps: false })
this.Project = this.sequelize.define('NiceProject', { title: DataTypes.STRING }, { timestamps: false })
this.User.hasMany(this.Project, { as: 'Projects' })
this.Project.belongsTo(this.User, { as: 'LovelyUser' })
this.User.sync({ force: true }).success(function() {
self.Project.sync({ force: true }).success(function() {
done()
})
})
})
it('returns an object containing all values', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(user.toJSON()).to.deep.equal({ username: 'test.user', age: 99, isAdmin: true, id: null })
done()
})
it('returns a response that can be stringified', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).to.deep.equal('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
done()
})
it('returns a response that can be stringified and then parsed', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.parse(JSON.stringify(user))).to.deep.equal({ username: 'test.user', age: 99, isAdmin: true, id: null })
done()
})
it('includes the eagerly loaded associations', function(done) {
var self = this
this.User.create({ username: 'fnord', age: 1, isAdmin: true }).success(function(user) {
self.Project.create({ title: 'fnord' }).success(function(project) {
user.setProjects([ project ]).success(function() {
self.User.findAll({include: [ { model: self.Project, as: 'Projects' } ]}).success(function(users) {
var _user = users[0]
expect(_user.projects).to.exist
expect(JSON.parse(JSON.stringify(_user)).projects).to.exist
self.Project.findAll({include: [ { model: self.User, as: 'LovelyUser' } ]}).success(function(projects) {
var _project = projects[0]
expect(_project.lovelyUser).to.exist
expect(JSON.parse(JSON.stringify(_project)).lovelyUser).to.exist
done()
})
})
})
})
})
})
})
describe('findAll', function() {
beforeEach(function(done) {
this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, { paranoid: true })
this.ParanoidUser.hasOne(this.ParanoidUser)
this.ParanoidUser.sync({ force: true }).success(function() {
done()
})
})
it("escapes a single single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user'name" })
.success(function() {
self.User.findAll({
where: { username: "user'name" }
}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("user'name")
done()
})
})
})
it("escapes two single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user''name" })
.success(function() {
self.User.findAll({
where: { username: "user''name" }
}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("user''name")
done()
})
})
})
it("returns the timestamps if no attributes have been specified", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
self.User.findAll().success(function(users) {
expect(users[0].createdAt).to.exist
done()
})
})
})
it("does not return the timestamps if the username attribute has been specified", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
self.User.findAll({ attributes: ['username'] }).success(function(users) {
expect(users[0].createdAt).not.to.exist
expect(users[0].username).to.exist
done()
})
})
})
it("creates the deletedAt property, when defining paranoid as true", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
expect(users[0].deletedAt).to.be.null
done()
})
})
})
it("sets deletedAt property to a specific date when deleting an instance", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
users[0].destroy().success(function(user) {
expect(user.deletedAt.getMonth).to.exist
done()
})
})
})
})
it("keeps the deletedAt-attribute with value null, when running updateAttributes", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
users[0].updateAttributes({username: 'newFnord'}).success(function(user) {
expect(user.deletedAt).to.be.null
done()
})
})
})
})
it("keeps the deletedAt-attribute with value null, when updating associations", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
self.ParanoidUser.create({ username: 'linkedFnord' }).success(function(linkedUser) {
users[0].setParanoidUser( linkedUser ).success(function(user) {
expect(user.deletedAt).to.be.null
done()
})
})
})
})
})
it("can reuse query option objects", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
self.User.findAll(query).success(function(users) {
expect(users[0].username).to.equal('fnord')
self.User.findAll(query).success(function(users) {
expect(users[0].username).to.equal('fnord')
done()
})
})
})
})
})
describe('find', function() {
it("can reuse query option objects", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
self.User.find(query).success(function(user) {
expect(user.username).to.equal('fnord')
self.User.find(query).success(function(user) {
expect(user.username).to.equal('fnord')
done()
})
})
})
})
})
describe('equals', function() {
it("can compare records with Date field", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function(user1) {
var query = { where: { username: 'fnord' }}
self.User.find(query).success(function(user2) {
expect(user1.equals(user2)).to.be.true
done()
})
})
})
})
describe('values', function() {
it('returns all values', function(done) {
var User = this.sequelize.define('UserHelper', {
username: DataTypes.STRING
}, { timestamps: false, logging: false })
User.sync().success(function() {
var user = User.build({ username: 'foo' })
expect(user.values).to.deep.equal({ username: "foo", id: null })
done()
})
})
})
describe('updateAttributes', function() {
it("updates attributes in the database", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.username).to.equal('user')
user.updateAttributes({ username: 'person' }).success(function(user) {
expect(user.username).to.equal('person')
done()
})
})
})
it("ignores unknown attributes", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.updateAttributes({ username: 'person', foo: 'bar'}).success(function(user) {
expect(user.username).to.equal('person')
expect(user.foo).not.to.exist
done()
})
})
})
it("doesn't update primary keys or timestamps", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
bio: DataTypes.TEXT,
identifier: {type: DataTypes.STRING, primaryKey: true}
})
User.sync({ force: true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var oldCreatedAt = user.createdAt
, oldIdentifier = user.identifier
user.updateAttributes({
name: 'foobar',
createdAt: new Date(2000, 1, 1),
identifier: 'another identifier'
}).success(function(user) {
expect((new Date(user.createdAt)).getTime()).to.equal((new Date(oldCreatedAt)).getTime())
expect(user.identifier).to.equal(oldIdentifier)
done()
})
})
})
})
it("uses primary keys in where clause", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
bio: DataTypes.TEXT,
identifier: {type: DataTypes.STRING, primaryKey: true}
})
User.sync({ force: true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var emitter = user.updateAttributes({name: 'foobar'})
emitter.on('sql', function(sql) {
expect(sql).to.match(/WHERE [`"]identifier[`"]..identifier./)
done()
})
})
})
})
it('stores and restores null values', function(done) {
var Download = this.sequelize.define('download', {
startedAt: DataTypes.DATE,
canceledAt: DataTypes.DATE,
finishedAt: DataTypes.DATE
})
Download.sync().success(function() {
Download.create({
startedAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt).to.not.be.ok
expect(download.finishedAt).to.not.be.ok
download.updateAttributes({
canceledAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt instanceof Date).to.be.true
expect(download.finishedAt).to.not.be.ok
Download.all({
where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL")
}).success(function(downloads) {
downloads.forEach(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt instanceof Date).to.be.true
expect(download.finishedAt).to.not.be.ok
done()
})
})
})
})
})
})
})
})
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Sequelize = require("../index") , Sequelize = require(__dirname + '/../index')
, Helpers = require('./buster-helpers') , Support = require(__dirname + '/support')
, dialect = Helpers.getTestDialect() , config = require(__dirname + '/config/config')
}
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { describe(Support.getTestDialectTeaser("DaoValidator"), function() {
describe('validations', function() { describe('validations', function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
onComplete: function(sequelize) {
this.sequelize = sequelize
done()
}.bind(this)
})
}) //- before
var checks = { var checks = {
is: { is: {
spec: { args: ["[a-z]",'i'] }, spec: { args: ["[a-z]",'i'] },
...@@ -116,72 +104,72 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -116,72 +104,72 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
fail: "a", fail: "a",
pass: "0" pass: "0"
} }
, len : { , len: {
spec: { args: [2,4] }, spec: { args: [2,4] },
fail: ["1", "12345"], fail: ["1", "12345"],
pass: ["12", "123", "1234"], pass: ["12", "123", "1234"],
raw: true raw: true
} }
, len: { , len$: {
spec: [2,4], spec: [2,4],
fail: ["1", "12345"], fail: ["1", "12345"],
pass: ["12", "123", "1234"], pass: ["12", "123", "1234"],
raw: true raw: true
} }
, isUUID : { , isUUID: {
spec: { args: 4 }, spec: { args: 4 },
fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479", fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479",
pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479" pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
} }
, isDate : { , isDate: {
fail: "not a date", fail: "not a date",
pass: "2011-02-04" pass: "2011-02-04"
} }
, isAfter : { , isAfter: {
spec: { args: "2011-11-05" }, spec: { args: "2011-11-05" },
fail: "2011-11-04", fail: "2011-11-04",
pass: "2011-11-05" pass: "2011-11-05"
} }
, isBefore : { , isBefore: {
spec: { args: "2011-11-05" }, spec: { args: "2011-11-05" },
fail: "2011-11-06", fail: "2011-11-06",
pass: "2011-11-05" pass: "2011-11-05"
} }
, isIn : { , isIn: {
spec: { args: "abcdefghijk" }, spec: { args: "abcdefghijk" },
fail: "ghik", fail: "ghik",
pass: "ghij" pass: "ghij"
} }
, notIn : { , notIn: {
spec: { args: "abcdefghijk" }, spec: { args: "abcdefghijk" },
fail: "ghij", fail: "ghij",
pass: "ghik" pass: "ghik"
} }
, max : { , max: {
spec: { args: 23 }, spec: { args: 23 },
fail: "24", fail: "24",
pass: "23" pass: "23"
} }
, max : { , max$: {
spec: 23, spec: 23,
fail: "24", fail: "24",
pass: "23" pass: "23"
} }
, min : { , min: {
spec: { args: 23 }, spec: { args: 23 },
fail: "22", fail: "22",
pass: "23" pass: "23"
} }
, min : { , min$: {
spec: 23, spec: 23,
fail: "22", fail: "22",
pass: "23" pass: "23"
} }
, isArray : { , isArray: {
fail: 22, fail: 22,
pass: [22] pass: [22]
} }
, isCreditCard : { , isCreditCard: {
fail: "401288888888188f", fail: "401288888888188f",
pass: "4012888888881881" pass: "4012888888881881"
} }
...@@ -189,6 +177,8 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -189,6 +177,8 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var validator in checks) { for (var validator in checks) {
if (checks.hasOwnProperty(validator)) { if (checks.hasOwnProperty(validator)) {
validator = validator.replace(/\$$/, '')
var validatorDetails = checks[validator] var validatorDetails = checks[validator]
if (!validatorDetails.hasOwnProperty("raw")) { if (!validatorDetails.hasOwnProperty("raw")) {
...@@ -202,7 +192,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -202,7 +192,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var i = 0; i < validatorDetails.fail.length; i++) { for (var i = 0; i < validatorDetails.fail.length; i++) {
var failingValue = validatorDetails.fail[i] var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() { it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
, message = validator + "(" + failingValue + ")" , message = validator + "(" + failingValue + ")"
...@@ -214,7 +204,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -214,7 +204,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
validations[validator].msg = message validations[validator].msg = message
var UserFail = this.sequelize.define('User' + Math.random(), { var UserFail = this.sequelize.define('User' + config.rand(), {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
validate: validations validate: validations
...@@ -224,18 +214,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -224,18 +214,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = UserFail.build({ name : failingValue }) var failingUser = UserFail.build({ name : failingValue })
, errors = failingUser.validate() , errors = failingUser.validate()
expect(errors).not.toBeNull() expect(errors).not.to.be.null
expect(errors).toEqual({ name : [message] }) expect(errors).to.eql({ name : [message] })
done()
}) })
} }
//////////////////////////// ////////////////////////////
// test the success cases // // test the success cases //
//////////////////////////// ////////////////////////////
for (var j = 0; j < validatorDetails.pass.length; j++) { for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j] var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() { it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
if (validatorDetails.hasOwnProperty('spec')) { if (validatorDetails.hasOwnProperty('spec')) {
...@@ -246,7 +238,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -246,7 +238,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
validations[validator].msg = validator + "(" + succeedingValue + ")" validations[validator].msg = validator + "(" + succeedingValue + ")"
var UserSuccess = this.sequelize.define('User' + Math.random(), { var UserSuccess = this.sequelize.define('User' + config.rand(), {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
validate: validations validate: validations
...@@ -254,14 +246,62 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -254,14 +246,62 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}) })
var successfulUser = UserSuccess.build({ name: succeedingValue }) var successfulUser = UserSuccess.build({ name: succeedingValue })
expect(successfulUser.validate()).toBeNull() expect(successfulUser.validate()).to.be.null
done()
}) })
} }
} }
} }
it('correctly validates using custom validation methods', function() { describe('#create', function() {
var User = this.sequelize.define('User' + Math.random(), { beforeEach(function(done) {
var self = this
var Project = this.sequelize.define('Project', {
name: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: 'unknown',
validate: {
isIn: [['unknown', 'hello', 'test']]
}
}
})
var Task = this.sequelize.define('Task', {
something: Sequelize.INTEGER
})
Project.hasOne(Task)
Task.hasOne(Project)
Project.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
self.Project = Project
self.Task = Task
done()
})
})
})
it('correctly validates using create method ', function(done) {
var self = this
this.Project.create({}).success(function(project) {
self.Task.create({something: 1}).success(function(task) {
project.setTask(task).success(function(task) {
expect(task.ProjectId).to.not.be.null
task.setProject(project).success(function(project) {
expect(project.ProjectId).to.not.be.null
done()
})
})
})
})
})
})
it('correctly validates using custom validation methods', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
validate: { validate: {
...@@ -277,15 +317,17 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -277,15 +317,17 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = User.build({ name : "3" }) var failingUser = User.build({ name : "3" })
, errors = failingUser.validate() , errors = failingUser.validate()
expect(errors).not.toBeNull(null) expect(errors).not.to.be.null
expect(errors).toEqual({ name: ["name should equal '2'"] }) expect(errors).to.eql({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" }) var successfulUser = User.build({ name : "2" })
expect(successfulUser.validate()).toBeNull() expect(successfulUser.validate()).to.be.null
done()
}) })
it('skips other validations if allowNull is true and the value is null', function() { it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + Math.random(), { var User = this.sequelize.define('User' + config.rand(), {
age: { age: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: true, allowNull: true,
...@@ -298,18 +340,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -298,18 +340,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = User.build({ age: -1 }) var failingUser = User.build({ age: -1 })
, errors = failingUser.validate() , errors = failingUser.validate()
expect(errors).not.toBeNull(null) expect(errors).not.to.be.null
expect(errors).toEqual({ age: ['must be positive'] }) expect(errors).to.eql({ age: ['must be positive'] })
var successfulUser1 = User.build({ age: null }) var successfulUser1 = User.build({ age: null })
expect(successfulUser1.validate()).toBeNull() expect(successfulUser1.validate()).to.be.null
var successfulUser2 = User.build({ age: 1 }) var successfulUser2 = User.build({ age: 1 })
expect(successfulUser2.validate()).toBeNull() expect(successfulUser2.validate()).to.be.null
done()
}) })
it('validates a model with custom model-wide validation methods', function() { it('validates a model with custom model-wide validation methods', function(done) {
var Foo = this.sequelize.define('Foo' + Math.random(), { var Foo = this.sequelize.define('Foo' + config.rand(), {
field1: { field1: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: true allowNull: true
...@@ -331,11 +375,12 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -331,11 +375,12 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingFoo = Foo.build({ field1: null, field2: null }) var failingFoo = Foo.build({ field1: null, field2: null })
, errors = failingFoo.validate() , errors = failingFoo.validate()
expect(errors).not.toBeNull() expect(errors).not.to.be.null
expect(errors).toEqual({ 'xnor': ['xnor failed'] }) expect(errors).to.eql({ 'xnor': ['xnor failed'] })
var successfulFoo = Foo.build({ field1: 33, field2: null }) var successfulFoo = Foo.build({ field1: 33, field2: null })
expect(successfulFoo.validate()).toBeNull() expect(successfulFoo.validate()).to.be.null
done()
}) })
}) })
}) })
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Sequelize = require("../index") , Sequelize = require(__dirname + '/../index')
, Helpers = require('./buster-helpers') , Support = require(__dirname + '/support')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose() chai.Assertion.includeStack = true
describe(Helpers.getTestDialectTeaser('DataTypes'), function() { describe(Support.getTestDialectTeaser('DataTypes'), function() {
it('should return DECIMAL for the default decimal type', function() { it('should return DECIMAL for the default decimal type', function(done) {
expect(Sequelize.DECIMAL).toEqual('DECIMAL'); expect(Sequelize.DECIMAL.toString()).to.equal('DECIMAL')
}); done()
})
it('should return DECIMAL(10,2) for the default decimal type with arguments', function() { it('should return DECIMAL(10,2) for the default decimal type with arguments', function(done) {
expect(Sequelize.DECIMAL(10, 2)).toEqual('DECIMAL(10,2)'); expect(Sequelize.DECIMAL(10, 2)).to.equal('DECIMAL(10,2)')
}); done()
})
var tests = [ var tests = [
[Sequelize.STRING, 'STRING', 'VARCHAR(255)'], [Sequelize.STRING, 'STRING', 'VARCHAR(255)'],
...@@ -59,8 +59,9 @@ describe(Helpers.getTestDialectTeaser('DataTypes'), function() { ...@@ -59,8 +59,9 @@ describe(Helpers.getTestDialectTeaser('DataTypes'), function() {
] ]
tests.forEach(function(test) { tests.forEach(function(test) {
it('transforms "' + test[1] + '" to "' + test[2] + '"', function() { it('transforms "' + test[1] + '" to "' + test[2] + '"', function(done) {
expect(test[0]).toEqual(test[2]) expect(test[0].toString()).to.equal(test[2])
done()
}) })
}) })
}) })
if(typeof require === 'function') { /* jshint camelcase: false */
const buster = require("buster") var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, sinon = require('sinon')
, CustomEventEmitter = require("../../lib/emitters/custom-event-emitter") , CustomEventEmitter = require("../../lib/emitters/custom-event-emitter")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() { describe(Support.getTestDialectTeaser("CustomEventEmitter"), function () {
describe("proxy", function () { describe("proxy", function () {
/* Tests could _probably_ be run synchronously, but for future proofing we're basing it on the events */ it("should correctly work with success listeners", function(done) {
it("should correctly work with success listeners", function (done) {
var emitter = new CustomEventEmitter() var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter() , proxy = new CustomEventEmitter()
, success = this.spy() , success = sinon.spy()
emitter.success(success) emitter.success(success)
proxy.success(function () { proxy.success(function () {
process.nextTick(function () { process.nextTick(function () {
expect(success.called).toEqual(true) expect(success.called).to.be.true
done(); done()
}) })
}) })
...@@ -29,16 +26,16 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() { ...@@ -29,16 +26,16 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() {
proxy.emit('success') proxy.emit('success')
}) })
it("should correctly work with error listeners", function (done) { it("should correctly work with error listeners", function(done) {
var emitter = new CustomEventEmitter() var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter() , proxy = new CustomEventEmitter()
, error = this.spy() , error = sinon.spy()
emitter.error(error) emitter.error(error)
proxy.error(function () { proxy.error(function() {
process.nextTick(function () { process.nextTick(function() {
expect(error.called).toEqual(true) expect(error.called).to.be.true
done(); done()
}) })
}) })
...@@ -46,21 +43,21 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() { ...@@ -46,21 +43,21 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() {
proxy.emit('error') proxy.emit('error')
}) })
it("should correctly work with complete/done listeners", function (done) { it("should correctly work with complete/done listeners", function(done) {
var emitter = new CustomEventEmitter() var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter() , proxy = new CustomEventEmitter()
, complete = this.spy() , complete = sinon.spy()
emitter.complete(complete) emitter.complete(complete)
proxy.complete(function () { proxy.complete(function() {
process.nextTick(function () { process.nextTick(function() {
expect(complete.called).toEqual(true) expect(complete.called).to.be.true
done(); done()
}) })
}) })
proxy.proxy(emitter) proxy.proxy(emitter)
proxy.emit('success') proxy.emit('success')
}) })
}); })
}); })
\ No newline at end of file
var chai = require('chai')
, expect = chai.expect
, Sequelize = require(__dirname + '/../index')
, Support = require(__dirname + '/support')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Language Util"), function() {
before(function(done) {
this.sequelize.options.language = 'es'
done()
})
after(function(done) {
this.sequelize.options.language = 'en'
done()
})
describe("Plural", function(){
it("should rename tables to their plural form...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
expect(table.tableName).to.equal('arboles')
expect(table2.tableName).to.equal('androides')
done()
})
it("should be able to pluralize/singularize associations...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
, table3 = self.sequelize.define('hombre', {name: Sequelize.STRING})
table.hasOne(table2)
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androides.identifier).to.equal('arbolId')
expect(table2.associations.arboles).to.exist
expect(table3.associations.androideshombres).to.exist
done()
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, Migrator = require("../lib/migrator")
, dialect = Support.getTestDialect()
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Migrator"), function() {
beforeEach(function() {
this.init = function(options, callback) {
options = Support.Sequelize.Utils._.extend({
path: __dirname + '/assets/migrations',
logging: function(){}
}, options || {})
// this.sequelize.options.logging = console.log
var migrator = new Migrator(this.sequelize, options)
migrator
.findOrCreateSequelizeMetaDAO({ force: true })
.success(function(SequelizeMeta) {
callback && callback(migrator, SequelizeMeta)
})
.error(function(err) { console.log(err) })
}.bind(this)
})
describe('getUndoneMigrations', function() {
it("returns no files if timestamps are after the files timestamp", function(done) {
this.init({ from: 20120101010101 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(0)
done()
})
})
})
it("returns only files between from and to", function(done) {
this.init({ from: 19700101000000, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(1)
expect(migrations[migrations.length - 1].filename).to.equal('20111117063700-createPerson.js')
done()
})
})
})
it("returns exactly the migration which is defined in from and to", function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(1)
expect(migrations[migrations.length - 1].filename).to.equal('20111117063700-createPerson.js')
done()
})
})
})
it("returns also the file which is exactly options.from or options.to", function(done) {
this.init({ from: 20111117063700, to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(2)
expect(migrations[0].filename).to.equal('20111117063700-createPerson.js')
expect(migrations[1].filename).to.equal('20111130161100-emptyMigration.js')
done()
})
})
})
it("returns all files to options.to if no options.from is defined", function(done) {
this.init({ to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(2)
done()
})
})
})
it("returns all files from last migration id stored in database", function(done) {
this.init(undefined, function(migrator, SequelizeMeta) {
SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(7)
expect(migrations[0].filename).to.equal('20111130161100-emptyMigration.js')
done()
})
})
})
})
})
describe('migrations', function() {
beforeEach(function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
self.migrator = migrator
self.migrator.migrate().success(done)
})
})
describe('executions', function() {
it("executes migration #20111117063700 and correctly creates the table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'Person' ])
done()
})
})
it("executes migration #20111117063700 and correctly adds isBetaMember", function(done) {
this.sequelize.getQueryInterface().describeTable('Person').success(function(data) {
var fields = Object.keys(data).sort()
expect(fields).to.eql([ 'isBetaMember', 'name' ])
done()
})
})
it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function(done) {
var self = this
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'Person' ])
self.migrator.migrate({ method: 'down' }).success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([])
done()
})
})
})
})
it("executes the empty migration #20111130161100", function(done) {
this.init({ from: 20111130161100, to: 20111130161100 }, function(migrator) {
// this migration isn't actually testing anything but
// should not timeout
// expect(1).to.equal(1)
migrator
.migrate()
.success(done)
.error(function(err) { console.log(err) })
})
})
})
describe('renameTable', function() {
beforeEach(function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
self.migrator = migrator
self.migrator.migrate().success(done)
})
})
it("executes migration #20111205064000 and renames a table", function(done) {
var self = this
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e) { return e != 'SequelizeMeta' })
expect(tableNames).to.include('Person')
self.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'User' ])
done()
})
})
})
})
})
})
describe('addColumn', function() {
it('adds a unique column to the user table', function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111205167000 }, function(migrator) {
migrator.migrate().complete(function(err) {
self.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).to.be.true
expect(isAdmin.allowNull).to.be.false
if (dialect === "postgres" || dialect === "postgres-native" || dialect === "sqlite") {
expect(isAdmin.defaultValue).to.be.false
} else {
expect(isAdmin.defaultValue).to.equal("0")
}
expect(shopId.allowNull).to.be.true
done()
})
})
})
})
it('adds a column to the user table', function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111205162700 }, function(migrator) {
migrator.migrate().complete(function(err) {
self.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).to.be.true
expect(isAdmin.allowNull).to.be.false
if (dialect === "postgres" || dialect === "postgres-native" || dialect === "sqlite") {
expect(isAdmin.defaultValue).to.be.false
} else {
expect(isAdmin.defaultValue).to.equal("0")
}
expect(shopId.allowNull).to.be.true
done()
})
})
})
})
})
describe('removeColumn', function() {
it('removes the shopId column from user', function(done) {
var self = this
this.init({ to: 20111206061400 }, function(migrator) {
migrator.migrate().success(function(){
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).to.be.true
expect(isAdmin.allowNull).to.be.false
if (dialect === "postgres" || dialect === "postgres-native" || dialect === "sqlite") {
expect(isAdmin.defaultValue).to.be.false
} else {
expect(isAdmin.defaultValue).to.equal("0")
}
expect(shopId).to.be.not.ok
done()
})
})
})
})
})
describe('changeColumn', function() {
it('changes the signature column from user to default "signature" + notNull', function(done) {
var self = this
this.init({ to: 20111206063000 }, function(migrator) {
migrator.migrate().success(function() {
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
if (dialect === 'postgres') {
expect(signature.type).to.equal('CHARACTER VARYING')
} else {
expect(signature.type).to.equal('VARCHAR(255)')
}
expect(signature.allowNull).to.equal(false)
expect(signature.defaultValue).to.equal('Signature')
done()
})
})
})
})
})
})
describe('renameColumn', function() {
it("renames the signature column from user to sig", function(done) {
var self = this
this.init({ to: 20111206163300 }, function(migrator) {
migrator.migrate().success(function(){
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, sig = data.sig
expect(signature).to.not.be.ok
expect(sig).to.be.ok
done()
})
})
})
})
})
})
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , dialect = Support.getTestDialect()
} , sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^postgres/)) { chai.Assertion.includeStack = true
describe('[POSTGRES] associations', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
if (dialect.match(/^mysql/)) {
describe('[MYSQL Specific] Associations', function() {
describe('many-to-many', function() { describe('many-to-many', function() {
describe('where tables have the same prefix', function() { describe('where tables have the same prefix', function() {
it("should create a table wp_table1wp_table2s", function(done) { it("should create a table wp_table1wp_table2s", function(done) {
var Table2 = this.sequelize.define('wp_table2', {foo: Helpers.Sequelize.STRING}) var Table2 = this.sequelize.define('wp_table2', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING}) , Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING})
, self = this
Table1.hasMany(Table2) Table1.hasMany(Table2)
Table2.hasMany(Table1) Table2.hasMany(Table1)
Table1.sync({ force: true }).success(function() {
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined() Table2.sync({ force: true }).success(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist
done() done()
}) })
}) })
})
})
describe('when join table name is specified', function() { describe('when join table name is specified', function() {
before(function(done){ beforeEach(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING}) var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING}) , Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'}) Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'}) Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
Table1.sync({ force: true }).success(function() {
Table2.sync({ force: true }).success(function() {
done() done()
}) })
})
it("should not use a combined name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.toBeDefined()
done()
}) })
it("should use the specified name", function(done) { it("should not use only a specified name", function() {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined() expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.to.exist
done() expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
}) })
}) })
}) })
describe('HasMany', function() { describe('HasMany', function() {
before(function(done) { beforeEach(function(done) {
//prevent periods from occurring in the table name since they are used to delimit (table.column) //prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING }) this.User = this.sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING }) this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: DataTypes.STRING })
this.users = null this.users = null
this.tasks = null this.tasks = null
...@@ -82,15 +72,19 @@ if (dialect.match(/^postgres/)) { ...@@ -82,15 +72,19 @@ if (dialect.match(/^postgres/)) {
tasks[tasks.length] = {name: 'Task' + Math.random()} tasks[tasks.length] = {name: 'Task' + Math.random()}
} }
this.sequelize.sync({ force: true }).success(function() { this.User.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(done) self.Task.bulkCreate(tasks).success(function() {
done()
})
})
}) })
}) })
}) })
describe('addDAO / getDAO', function() { describe('addDAO / getDAO', function() {
before(function(done) { beforeEach(function(done) {
var self = this var self = this
self.user = null self.user = null
...@@ -109,10 +103,10 @@ if (dialect.match(/^postgres/)) { ...@@ -109,10 +103,10 @@ if (dialect.match(/^postgres/)) {
var self = this var self = this
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0) expect(_tasks.length).to.equal(0)
self.user.addTask(self.task).on('success', function() { self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1) expect(_tasks.length).to.equal(1)
done() done()
}) })
}) })
...@@ -121,7 +115,7 @@ if (dialect.match(/^postgres/)) { ...@@ -121,7 +115,7 @@ if (dialect.match(/^postgres/)) {
}) })
describe('removeDAO', function() { describe('removeDAO', function() {
before(function(done) { beforeEach(function(done) {
var self = this var self = this
self.user = null self.user = null
...@@ -140,13 +134,13 @@ if (dialect.match(/^postgres/)) { ...@@ -140,13 +134,13 @@ if (dialect.match(/^postgres/)) {
var self = this var self = this
self.user.getTasks().on('success', function(__tasks) { self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0) expect(__tasks.length).to.equal(0)
self.user.setTasks(self.tasks).on('success', function() { self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length) expect(_tasks.length).to.equal(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() { self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1) expect(_tasks.length).to.equal(self.tasks.length - 1)
done() done()
}) })
}) })
......
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , dialect = Support.getTestDialect()
} , sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql/)) {
describe('[MYSQL] Connector Manager', function() { describe('[MYSQL Specific] Connector Manager', function() {
before(function(done) { this.timeout(10000)
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
it('works correctly after being idle', function(done) { it('works correctly after being idle', function(done) {
this.timeout = 1000 * 10 var User = this.sequelize.define('User', { username: DataTypes.STRING })
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING }) , spy = sinon.spy()
, spy = this.spy()
User.sync({force: true}).on('success', function() { User.sync({force: true}).on('success', function() {
User.create({username: 'user1'}).on('success', function() { User.create({username: 'user1'}).on('success', function() {
User.count().on('success', function(count) { User.count().on('success', function(count) {
expect(count).toEqual(1) expect(count).to.equal(1)
spy() spy()
setTimeout(function() { setTimeout(function() {
User.count().on('success', function(count) { User.count().on('success', function(count) {
expect(count).toEqual(1) expect(count).to.equal(1)
spy() spy()
if (spy.calledTwice) { if (spy.calledTwice) {
done() done()
......
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe("[MYSQL Specific] DAOFactory", function () {
describe('constructor', function() {
it("handles extended attributes (unique)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: { type: DataTypes.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (default)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (null)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (comment)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (primaryKey)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) PRIMARY KEY"})
done()
})
it("adds timestamps", function(done) {
var User1 = this.sequelize.define('User' + config.rand(), {})
var User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true })
expect(User1.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
expect(User2.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("adds deletedAt if paranoid", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("underscores timestamps if underscored", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
done()
})
it('omits text fields with defaultValues', function(done) {
var User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.TEXT, defaultValue: 'helloworld'}})
expect(User.attributes.name).to.equal('TEXT')
done()
})
it('omits blobs fields with defaultValues', function(done) {
var User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.STRING.BINARY, defaultValue: 'helloworld'}})
expect(User.attributes.name).to.equal('VARCHAR(255) BINARY')
done()
})
})
describe('validations', function() {
describe('enums', function() {
it('enum data type should be case insensitive if my collation allows it', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
mood: {
type: DataTypes.ENUM,
values: ['HAPPY', 'sad', 'WhatEver']
}
}, {
collate: 'utf8_general_ci'
})
User.sync({ force: true }).success(function() {
User.create({mood: 'happy'}).success(function(user) {
expect(user).to.exist
expect(user.mood).to.equal('HAPPY')
var u = User.build({mood: 'SAD'})
u.save().success(function(_user) {
expect(_user).to.exist
expect(_user.mood).to.equal('sad')
done()
})
})
})
})
it('enum data type should be case sensitive if my collation enforces it', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
mood: {
type: DataTypes.ENUM,
values: ['HAPPY', 'sad', 'WhatEver']
}
}, {
collate: 'latin1_bin'
})
User.sync({ force: true }).success(function() {
expect(function() {
User.create({mood: 'happy'})
}).to.throw(Error, 'Value "happy" for ENUM mood is out of allowed scope. Allowed values: HAPPY, sad, WhatEver')
expect(function() {
var u = User.build({mood: 'SAD'})
u.save()
}).to.throw(Error, 'Value "SAD" for ENUM mood is out of allowed scope. Allowed values: HAPPY, sad, WhatEver')
done()
})
})
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
foo: {type: DataTypes.STRING, primaryKey: true},
bar: DataTypes.STRING
})
expect(User.primaryKeys).to.deep.equal({"foo":"VARCHAR(255) PRIMARY KEY"})
done()
})
})
})
}
/* jshint camelcase: false */ /* jshint camelcase: false */
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, config = require('../config/config') , Support = require(__dirname + '/../support')
, Helpers = require('../buster-helpers') , dialect = Support.getTestDialect()
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
, util = require("util") , util = require("util")
, _ = require('lodash')
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
} chai.Assertion.includeStack = true
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) { if (dialect.match(/^mysql/)) {
describe('[MYSQL] QueryGenerator', function() { describe("[MYSQL Specific] QueryGenerator", function () {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
var suites = { var suites = {
attributesToSQL: [ attributesToSQL: [
{ {
...@@ -85,7 +68,7 @@ if (dialect.match(/^mysql/)) { ...@@ -85,7 +68,7 @@ if (dialect.match(/^mysql/)) {
{ {
arguments: [{id: {type: 'INTEGER', allowNull: false, autoIncrement: true, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}], arguments: [{id: {type: 'INTEGER', allowNull: false, autoIncrement: true, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}],
expectation: {id: 'INTEGER NOT NULL auto_increment DEFAULT 1 REFERENCES `Bar` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT'} expectation: {id: 'INTEGER NOT NULL auto_increment DEFAULT 1 REFERENCES `Bar` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT'}
}, }
], ],
createTableQuery: [ createTableQuery: [
...@@ -425,7 +408,7 @@ if (dialect.match(/^mysql/)) { ...@@ -425,7 +408,7 @@ if (dialect.match(/^mysql/)) {
] ]
} }
Helpers.Sequelize.Utils._.each(suites, function(tests, suiteTitle) { _.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() { describe(suiteTitle, function() {
tests.forEach(function(test) { tests.forEach(function(test) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
...@@ -434,7 +417,7 @@ if (dialect.match(/^mysql/)) { ...@@ -434,7 +417,7 @@ if (dialect.match(/^mysql/)) {
var context = test.context || {options: {}}; var context = test.context || {options: {}};
QueryGenerator.options = context.options QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
}) })
}) })
......
/* jshint camelcase: false */ var chai = require('chai')
if(typeof require === 'function') { , expect = chai.expect
const buster = require("buster") , Support = require(__dirname + '/../support')
, config = require('../config/config') , dialect = Support.getTestDialect()
, Helpers = require('../buster-helpers') , config = require(__dirname + '/../config/config')
, dialect = Helpers.getTestDialect() , DataTypes = require(__dirname + "/../../lib/data-types")
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] Associations', function() {
before(function(done) {
var self = this
Helpers.initTests({ chai.Assertion.includeStack = true
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] associations', function() {
describe('many-to-many', function() { describe('many-to-many', function() {
describe('where tables have the same prefix', function() { describe('where tables have the same prefix', function() {
it("should create a table wp_table1wp_table2s", function(done) { it("should create a table wp_table1wp_table2s", function(done) {
var Table2 = this.sequelize.define('wp_table2', {foo: Helpers.Sequelize.STRING}) var Table2 = this.sequelize.define('wp_table2', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING}) , Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING})
, self = this
Table1.hasMany(Table2) Table1.hasMany(Table2)
Table2.hasMany(Table1) Table2.hasMany(Table1)
this.sequelize.sync({ force: true }).success(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined() expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist
done() done()
}) })
}) })
})
describe('when join table name is specified', function() { describe('when join table name is specified', function() {
before(function(done){ beforeEach(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING}) var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING}) , Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'}) Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'}) Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
this.sequelize.sync({ force: true }).success(done) done()
}) })
it("should not use a combined name", function(done) { it("should not use a combined name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.toBeDefined() expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.to.exist
done() done()
}) })
it("should use the specified name", function(done) { it("should use the specified name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined() expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
done() done()
}) })
}) })
}) })
describe('HasMany', function() { describe('HasMany', function() {
before(function(done) { describe('addDAO / getDAO', function() {
beforeEach(function(done) {
var self = this
//prevent periods from occurring in the table name since they are used to delimit (table.column) //prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING }) this.User = this.sequelize.define('User' + config.rand(), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING }) this.Task = this.sequelize.define('Task' + config.rand(), { name: DataTypes.STRING })
this.users = null this.users = null
this.tasks = null this.tasks = null
...@@ -87,20 +71,10 @@ if (dialect.match(/^mysql/)) { ...@@ -87,20 +71,10 @@ if (dialect.match(/^mysql/)) {
tasks[tasks.length] = {name: 'Task' + Math.random()} tasks[tasks.length] = {name: 'Task' + Math.random()}
} }
this.sequelize.sync({ force: true }).success(function() { self.User.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(done) self.Task.bulkCreate(tasks).success(function() {
})
})
})
describe('addDAO / getDAO', function() {
before(function(done) {
var self = this
self.user = null
self.task = null
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]
...@@ -109,15 +83,19 @@ if (dialect.match(/^mysql/)) { ...@@ -109,15 +83,19 @@ if (dialect.match(/^mysql/)) {
}) })
}) })
}) })
})
})
})
})
it('should correctly add an association to the dao', function(done) { it('should correctly add an association to the dao', function(done) {
var self = this var self = this
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0) expect(_tasks).to.have.length(0)
self.user.addTask(self.task).on('success', function() { self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1) expect(_tasks).to.have.length(1)
done() done()
}) })
}) })
...@@ -126,32 +104,47 @@ if (dialect.match(/^mysql/)) { ...@@ -126,32 +104,47 @@ if (dialect.match(/^mysql/)) {
}) })
describe('removeDAO', function() { describe('removeDAO', function() {
before(function(done) { it("should correctly remove associated objects", function(done) {
var self = this var self = this
, users = []
, tasks = []
//prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + config.rand(), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + config.rand(), { name: DataTypes.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
self.user = null for (var i = 0; i < 5; ++i) {
self.tasks = null users[users.length] = {id: i, name: 'User' + Math.random()}
}
for (var x = 0; x < 5; ++x) {
tasks[tasks.length] = {id: i, name: 'Task' + Math.random()}
}
self.User.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.User.bulkCreate(users).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.users = _users
self.tasks = _tasks self.tasks = _tasks
done()
})
})
})
it("should correctly remove associated objects", function(done) {
var self = this
self.user.getTasks().on('success', function(__tasks) { self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0) expect(__tasks).to.have.length(0)
self.user.setTasks(self.tasks).on('success', function() { self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length) expect(_tasks).to.have.length(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() { self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) { self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1) expect(_tasks).to.have.length(self.tasks.length - 1)
done() done()
}) })
}) })
...@@ -162,4 +155,10 @@ if (dialect.match(/^mysql/)) { ...@@ -162,4 +155,10 @@ if (dialect.match(/^mysql/)) {
}) })
}) })
}) })
})
})
})
})
})
})
} }
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , dialect = Support.getTestDialect()
} , config = require(__dirname + '/../config/config')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose() chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) { if (dialect.match(/^postgres/)) {
describe('[POSTGRES] DAO', function() { describe('[POSTGRES Specific] DAO', function() {
before(function(done) { beforeEach(function(done) {
var self = this this.sequelize.options.quoteIdentifiers = true
this.User = this.sequelize.define('User', {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)}, email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'} document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
}) })
}, this.User.sync({ force: true }).success(function() {
onComplete: function() { done()
self.User.sync({ force: true }).success(done) })
}
}) })
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
done()
}) })
describe('model', function() { describe('model', function() {
it("create handles array correctly", function(done) { it("create handles array correctly", function(done) {
var self = this
this.User this.User
.create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] }) .create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] })
.success(function(oldUser) { .success(function(oldUser) {
expect(oldUser.email).toEqual(['foo@bar.com', 'bar@baz.com']) expect(oldUser.email).to.contain.members(['foo@bar.com', 'bar@baz.com'])
done() done()
}) })
.error(function(err) { .error(function(err) {
...@@ -49,15 +45,15 @@ if (dialect.match(/^postgres/)) { ...@@ -49,15 +45,15 @@ if (dialect.match(/^postgres/)) {
this.User this.User
.create({ username: 'user', email: ['foo@bar.com'], document: { created: { test: '"value"' }}}) .create({ username: 'user', email: ['foo@bar.com'], document: { created: { test: '"value"' }}})
.success(function(newUser) { .success(function(newUser) {
expect(newUser.document).toEqual({ created: { test: '"value"' }}) expect(newUser.document).to.deep.equal({ created: { test: '"value"' }})
// Check to see if updating an hstore field works // Check to see if updating an hstore field works
newUser.updateAttributes({document: {should: 'update', to: 'this', first: 'place'}}).success(function(oldUser){ newUser.updateAttributes({document: {should: 'update', to: 'this', first: 'place'}}).success(function(oldUser){
// Postgres always returns keys in alphabetical order (ascending) // Postgres always returns keys in alphabetical order (ascending)
expect(oldUser.document).toEqual({first: 'place', should: 'update', to: 'this'}) expect(oldUser.document).to.deep.equal({first: 'place', should: 'update', to: 'this'})
// Check to see if the default value for an hstore field works // Check to see if the default value for an hstore field works
self.User.create({ username: 'user2', email: ['bar@baz.com']}).success(function(defaultUser){ self.User.create({ username: 'user2', email: ['bar@baz.com']}).success(function(defaultUser){
expect(defaultUser.document).toEqual({default: 'value'}) expect(defaultUser.document).to.deep.equal({default: 'value'})
done() done()
}) })
}) })
...@@ -65,60 +61,46 @@ if (dialect.match(/^postgres/)) { ...@@ -65,60 +61,46 @@ if (dialect.match(/^postgres/)) {
.error(console.log) .error(console.log)
}) })
}) })
})
describe('[POSTGRES] Unquoted identifiers', function() { describe('[POSTGRES] Unquoted identifiers', function() {
it("can insert and select", function(done) {
before(function(done) {
var self = this var self = this
this.sequelize.options.quoteIdentifiers = false
this.sequelize.getQueryInterface().QueryGenerator.options.quoteIdentifiers = false
Helpers.initTests({ this.User = this.sequelize.define('Userxs', {
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.sequelize.options.quoteIdentifiers = false
self.User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
fullName: DataTypes.STRING // Note mixed case fullName: DataTypes.STRING // Note mixed case
}, {
quoteIdentifiers: false
}) })
},
onComplete: function() {
// We can create a table with non-quoted identifiers
self.User.sync({ force: true }).success(done)
}
})
})
it("can insert and select", function(done) {
var self = this
this.User.sync({ force: true }).success(function() {
self.User self.User
.create({ username: 'user', fullName: "John Smith" }) .create({ username: 'user', fullName: "John Smith" })
.success(function(user) { .success(function(user) {
// We can insert into a table with non-quoted identifiers // We can insert into a table with non-quoted identifiers
expect(user.id).toBeDefined() expect(user.id).to.exist
expect(user.id).not.toBeNull() expect(user.id).not.to.be.null
expect(user.username).toEqual('user') expect(user.username).to.equal('user')
expect(user.fullName).toEqual('John Smith') expect(user.fullName).to.equal('John Smith')
// We can query by non-quoted identifiers // We can query by non-quoted identifiers
self.User.find({ self.User.find({
where: {fullName: "John Smith"} where: {fullName: "John Smith"}
}) })
.success(function(user2) { .success(function(user2) {
self.sequelize.options.quoteIndentifiers = true
self.sequelize.getQueryInterface().QueryGenerator.options.quoteIdentifiers = true
self.sequelize.options.logging = false
// We can map values back to non-quoted identifiers // We can map values back to non-quoted identifiers
expect(user2.id).toEqual(user.id) expect(user2.id).to.equal(user.id)
expect(user2.username).toEqual('user') expect(user2.username).to.equal('user')
expect(user2.fullName).toEqual('John Smith') expect(user2.fullName).to.equal('John Smith')
done(); done()
}) })
.error(function(err) {
console.log(err)
}) })
}) })
.error(function(err) {
console.log(err)
}) })
}) })
}) })
......
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, hstore = require(__dirname + '/../../lib/dialects/postgres/hstore')
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] hstore', function() {
describe('stringifyPart', function() {
it("handles undefined values correctly", function(done) {
expect(hstore.stringifyPart(undefined)).to.equal('NULL')
done()
})
it("handles null values correctly", function(done) {
expect(hstore.stringifyPart(null)).to.equal('NULL')
done()
})
it("handles boolean values correctly", function(done) {
expect(hstore.stringifyPart(false)).to.equal('false')
expect(hstore.stringifyPart(true)).to.equal('true')
done()
})
it("handles strings correctly", function(done) {
expect(hstore.stringifyPart('foo')).to.equal('"foo"')
done()
})
it("handles strings with backslashes correctly", function(done) {
expect(hstore.stringifyPart("\\'literally\\'")).to.equal('"\\\\\'literally\\\\\'"')
done()
})
it("handles arrays correctly", function(done) {
expect(hstore.stringifyPart([1,['2'],'"3"'])).to.equal('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
done()
})
it("handles simple objects correctly", function(done) {
expect(hstore.stringifyPart({ test: 'value' })).to.equal('"{\\"test\\":\\"value\\"}"')
done()
})
it("handles nested objects correctly", function(done) {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).to.equal('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
done()
})
it("handles objects correctly", function(done) {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).to.equal('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
done()
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.stringify({ })).to.equal('')
done()
})
it('should handle null values correctly', function(done) {
expect(hstore.stringify({ null: null })).to.equal('"null"=>NULL')
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.stringify({ test: 'value' })).to.equal('"test"=>"value"')
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.stringify({ test: { nested: 'value' } })).to.equal('"test"=>"{\\"nested\\":\\"value\\"}"')
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).to.equal('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).to.equal('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
done()
})
})
describe('parse', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.parse('')).to.deep.equal({ })
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.parse('"test"=>"value"')).to.deep.equal({ test: 'value' })
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).to.deep.equal({ test: { nested: 'value' } })
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).to.deep.equal({ test: [ 1, '2', [ '"3"' ] ] })
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: "1", array: [1,'2'], object: { object: 'value' }})
done()
})
})
})
}
if(typeof require === 'function') { /* jshint camelcase: false */
const buster = require("buster") var chai = require('chai')
, Helpers = require('../buster-helpers') , expect = chai.expect
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/postgres/query-generator") , QueryGenerator = require("../../lib/dialects/postgres/query-generator")
, util = require("util") , Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, DataTypes = require(__dirname + "/../../lib/data-types")
, moment = require('moment') , moment = require('moment')
} , util = require("util")
, _ = require('lodash')
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
if (dialect.match(/^postgres/)) { if (dialect.match(/^postgres/)) {
describe('[POSTGRES] QueryGenerator', function() { describe('[POSTGRES Specific] QueryGenerator', function() {
before(function(done) { beforeEach(function(done) {
var self = this this.User = this.sequelize.define('User', {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)}, email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'} document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
}) })
}, this.User.sync({ force: true }).success(function() {
onComplete: function() { done()
self.User.sync({ force: true }).success(done)
}
}) })
}) })
...@@ -776,8 +768,14 @@ if (dialect.match(/^postgres/)) { ...@@ -776,8 +768,14 @@ if (dialect.match(/^postgres/)) {
] ]
} }
Helpers.Sequelize.Utils._.each(suites, function(tests, suiteTitle) { _.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() { describe(suiteTitle, function() {
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
QueryGenerator.options.quoteIdentifiers = true
done()
})
tests.forEach(function(test) { tests.forEach(function(test) {
var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) var title = test.title || 'Postgres correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
it(title, function(done) { it(title, function(done) {
...@@ -785,7 +783,7 @@ if (dialect.match(/^postgres/)) { ...@@ -785,7 +783,7 @@ if (dialect.match(/^postgres/)) {
var context = test.context || {options: {}}; var context = test.context || {options: {}};
QueryGenerator.options = context.options QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
}) })
}) })
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Promise"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1, 20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
this.User.sync({ force: true }).then(function() { done() })
})
describe('increment', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
})
it('with array', function(done) {
var self = this
this.User
.find(1)
.then(function(user) {
expect(user.id).to.equal(1)
return user.increment(['aNumber'], 2)
})
.then(function(user) {
// The following assertion would rock hard, but it's not implemented :(
// expect(user.aNumber).to.equal(2)
return self.User.find(1)
})
.then(function(user) {
expect(user.aNumber).to.equal(2)
done()
})
});
it('should still work right with other concurrent updates', function(done) {
var self = this
// Select something
this.User
.find(1)
.then(function (user1) {
// Select the user again (simulating a concurrent query)
return self.User.find(1)
.then(function (user2) {
return user2
.updateAttributes({ aNumber: user2.aNumber + 1 })
.then(function() { return user1.increment(['aNumber'], 2) })
.then(function() { return self.User.find(1) })
.then(function(user5) {
expect(user5.aNumber).to.equal(3)
done()
})
})
})
})
it('with key value pair', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.increment({ 'aNumber': 1, 'bNumber': 2})
})
.then(function () {
return self.User.find(1)
})
.then(function (user3) {
expect(user3.aNumber).to.equal(1)
expect(user3.bNumber).to.equal(2)
done()
})
})
})
describe('decrement', function () {
beforeEach(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
})
it('with array', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.decrement(['aNumber'], 2)
})
.then(function(user2) {
return self.User.find(1)
})
.then(function(user3) {
expect(user3.aNumber).to.equal(-2)
done()
})
})
it('with single field', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.decrement(['aNumber'], 2)
})
.then(function(user3) {
return self.User.find(1)
})
.then(function (user3) {
expect(user3.aNumber).to.equal(-2)
done()
})
})
it('should still work right with other concurrent decrements', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
var _done = _.after(3, function() {
self.User
.find(1)
.then(function(user2) {
expect(user2.aNumber).to.equal(-6)
done()
})
})
user1.decrement(['aNumber'], 2).done(_done)
user1.decrement(['aNumber'], 2).done(_done)
user1.decrement(['aNumber'], 2).done(_done)
})
})
})
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function(done) {
this.User
.create({ username: 'John Doe' })
.then(function(originalUser) {
return originalUser
.updateAttributes({ username: 'Doe John' })
.then(function () {
return originalUser.reload()
})
.then(function(updatedUser) {
expect(originalUser === updatedUser).to.be.true
done()
})
})
})
it("should update the values on all references to the DAO", function(done) {
var self = this
this.User
.create({ username: 'John Doe' })
.then(function(originalUser) {
return self.User
.find(originalUser.id)
.then(function(updater) {
return updater.updateAttributes({ username: 'Doe John' })
})
.then(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).to.equal('John Doe')
return originalUser.reload()
}).then(function(updatedUser) {
expect(originalUser.username).to.equal('Doe John')
expect(updatedUser.username).to.equal('Doe John')
done()
})
})
})
it("should update the associations as well", function (done) {
var Book = this.sequelize.define('Book', { title: DataTypes.STRING })
, Page = this.sequelize.define('Page', { content: DataTypes.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
Book
.sync({ force: true })
.then(function() {
Page
.sync({ force: true })
.then(function() {
return Book.create({ title: 'A very old book' })
})
.then(function (book) {
return Page
.create({ content: 'om nom nom' })
.then(function(page) {
return book
.setPages([ page ])
.then(function() {
return Book
.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
})
.then(function (leBook) {
return page
.updateAttributes({ content: 'something totally different' })
.then(function (page) {
expect(leBook.pages[0].content).to.equal('om nom nom')
expect(page.content).to.equal('something totally different')
return leBook
.reload()
.then(function (leBook) {
expect(leBook.pages[0].content).to.equal('something totally different')
expect(page.content).to.equal('something totally different')
done()
})
})
})
})
})
}, done)
})
})
})
describe('complete', function () {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).then(null, function(err) {
expect(err).to.be.ok
expect(err.message).to.be.ok
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().then(function(result) {
expect(result).to.not.be.undefined
done()
})
})
})
describe('save', function () {
it('should fail a validation upon creating', function(done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.greaterThan(-1)
done()
});
})
it('should fail a validation upon building', function(done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateCustom).to.be.ok
expect(err.validateCustom).to.be.an("array")
expect(err.validateCustom[0]).to.be.ok
expect(err.validateCustom[0]).to.equal('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.ok
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer:')).to.be.greaterThan(-1)
done()
})
})
})
})
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Support = require(__dirname + '/support')
, QueryChainer = require("../lib/query-chainer") , QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter") , CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { describe(Support.getTestDialectTeaser("QueryChainer"), function () {
before(function() { beforeEach(function(done) {
this.queryChainer = new QueryChainer() this.queryChainer = new QueryChainer()
done()
}) })
describe('add', function() { describe('add', function() {
it('adds a new serial item if method is passed', function() { it('adds a new serial item if method is passed', function(done) {
expect(this.queryChainer.serials.length).toEqual(0) expect(this.queryChainer.serials.length).to.equal(0)
this.queryChainer.add({}, 'foo') this.queryChainer.add({}, 'foo')
expect(this.queryChainer.serials.length).toEqual(1) expect(this.queryChainer.serials.length).to.equal(1)
done()
}) })
it('adds a new emitter if no method is passed', function() { it('adds a new emitter if no method is passed', function(done) {
expect(this.queryChainer.emitters.length).toEqual(0) expect(this.queryChainer.emitters.length).to.equal(0)
this.queryChainer.add(new CustomEventEmitter()) this.queryChainer.add(new CustomEventEmitter())
expect(this.queryChainer.emitters.length).toEqual(1) expect(this.queryChainer.emitters.length).to.equal(1)
done()
}) })
}) })
...@@ -37,7 +37,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -37,7 +37,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2) this.queryChainer.add(emitter2)
this.queryChainer.run().success(function() { this.queryChainer.run().success(function() {
assert(true) expect(true).to.be.true
done() done()
}).error(function(err) { }).error(function(err) {
console.log(err) console.log(err)
...@@ -53,9 +53,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -53,9 +53,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter1) this.queryChainer.add(emitter1)
this.queryChainer.run().success(function(results) { this.queryChainer.run().success(function(results) {
expect(results).toBeDefined() expect(results).to.exist
expect(results.length).toEqual(1) expect(results).to.have.length(1)
expect(results[0]).toEqual(1) expect(results[0]).to.equal(1)
done() done()
}) })
...@@ -72,8 +72,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -72,8 +72,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter3) this.queryChainer.add(emitter3)
this.queryChainer.run().success(function(results) { this.queryChainer.run().success(function(results) {
expect(results.length).toEqual(3) expect(results).to.have.length(3)
expect(results).toEqual([1,2,3]) expect(results).to.include.members([1,2,3])
done() done()
}) })
...@@ -90,10 +90,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -90,10 +90,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2) this.queryChainer.add(emitter2)
this.queryChainer.run().success(function(results, result1, result2) { this.queryChainer.run().success(function(results, result1, result2) {
expect(result1).toBeDefined() expect(result1).to.exist
expect(result2).toBeDefined() expect(result2).to.exist
expect(result1).toEqual(1) expect(result1).to.equal(1)
expect(result2).toEqual(2) expect(result2).to.equal(2)
done() done()
}) })
...@@ -111,7 +111,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -111,7 +111,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2, 'run') this.queryChainer.add(emitter2, 'run')
this.queryChainer.runSerially().success(function() { this.queryChainer.runSerially().success(function() {
assert(true) expect(true).to.be.true
done() done()
}) })
}) })
...@@ -122,9 +122,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -122,9 +122,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter1, 'run') this.queryChainer.add(emitter1, 'run')
this.queryChainer.runSerially().success(function(results) { this.queryChainer.runSerially().success(function(results) {
expect(results).toBeDefined() expect(results).to.exist
expect(results.length).toEqual(1) expect(results).to.have.length(1)
expect(results[0]).toEqual(1) expect(results[0]).to.equal(1)
done() done()
}) })
}) })
...@@ -139,8 +139,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -139,8 +139,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter3, 'run') this.queryChainer.add(emitter3, 'run')
this.queryChainer.runSerially().success(function(results) { this.queryChainer.runSerially().success(function(results) {
expect(results.length).toEqual(3) expect(results).to.have.length(3)
expect(results).toEqual([1,2,3]) expect(results).to.contain.members([1,2,3])
done() done()
}) })
}) })
...@@ -153,10 +153,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() { ...@@ -153,10 +153,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2, 'run') this.queryChainer.add(emitter2, 'run')
this.queryChainer.runSerially().success(function(results, result1, result2) { this.queryChainer.runSerially().success(function(results, result1, result2) {
expect(result1).toBeDefined() expect(result1).to.exist
expect(result2).toBeDefined() expect(result2).to.exist
expect(result1).toEqual(1) expect(result1).to.equal(1)
expect(result2).toEqual(2) expect(result2).to.equal(2)
done() done()
}) })
}) })
......
/* jshint multistr: true */ /* jshint multistr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
if (typeof require === 'function') { chai.Assertion.includeStack = true
var buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 2000
describe(Helpers.getTestDialectTeaser("QueryGenerators"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
this.sequelize = sequelize
this.DataTypes = DataTypes
}.bind(this),
onComplete: function() {
this.interface = this.sequelize.getQueryInterface()
done()
}.bind(this)
})
})
describe(Support.getTestDialectTeaser("QueryGenerators"), function () {
describe("comments", function() { describe("comments", function() {
it("should create a comment for a column", function(done) { it("should create a comment for a column", function(done) {
var self = this var self = this
, User = this.sequelize.define('User', { , User = this.sequelize.define('User', {
username: {type: this.DataTypes.STRING, comment: 'Some lovely info for my DBA'} username: {type: DataTypes.STRING, comment: 'Some lovely info for my DBA'}
}) })
User.sync().success(function(){ User.sync({ force: true }).success(function() {
var sql = '' var sql = ''
if (dialect === "mysql") { if (dialect === "mysql") {
sql = 'SELECT COLUMN_COMMENT as cmt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \'' + self.sequelize.config.database + '\' AND TABLE_NAME = \'Users\' AND COLUMN_NAME = \'username\''; sql = 'SELECT COLUMN_COMMENT as cmt FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = \'' + self.sequelize.config.database + '\' AND TABLE_NAME = \'Users\' AND COLUMN_NAME = \'username\'';
...@@ -45,16 +30,16 @@ describe(Helpers.getTestDialectTeaser("QueryGenerators"), function() { ...@@ -45,16 +30,16 @@ describe(Helpers.getTestDialectTeaser("QueryGenerators"), function() {
} }
else if (dialect === "sqlite") { else if (dialect === "sqlite") {
// sqlite doesn't support comments except for explicit comments in the file // sqlite doesn't support comments except for explicit comments in the file
expect(true).toBeTrue() expect(true).to.be.true
return done() return done()
} else { } else {
console.log('FIXME: This dialect is not supported :('); console.log('FIXME: This dialect is not supported :(');
expect(true).toBeTrue() expect(true).to.be.true
return done() return done()
} }
self.sequelize.query(sql, null, {raw: true}).success(function(result) { self.sequelize.query(sql, null, {raw: true}).success(function(result) {
expect(result[0].cmt).toEqual('Some lovely info for my DBA'); expect(result[0].cmt).to.equal('Some lovely info for my DBA');
done() done()
}) })
}) })
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("QueryInterface"), function () {
beforeEach(function(done) {
this.sequelize.options.quoteIdenifiers = true
this.queryInterface = this.sequelize.getQueryInterface()
done()
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
var self = this
this.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
self.queryInterface.createTable('table', { name: DataTypes.STRING }).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.have.length(1)
self.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
done()
})
})
})
})
})
})
})
})
describe('indexes', function() {
beforeEach(function(done) {
var self = this
this.queryInterface.dropTable('Users').success(function() {
self.queryInterface.createTable('Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN
}).success(function() {
done()
})
})
})
it('adds, reads and removes an index to the table', function(done) {
var self = this
this.queryInterface.addIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
var indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.include('users_username_is_admin')
self.queryInterface.removeIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.be.empty
done()
})
})
})
})
})
})
describe('describeTable', function() {
it('reads the metadata of the table', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN,
enumVals: DataTypes.ENUM('hello', 'world')
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
expect(err).to.be.null
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).to.equal(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).to.be.true
expect(username.defaultValue).to.be.null
expect(isAdmin.type).to.equal(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).to.be.true
expect(isAdmin.defaultValue).to.be.null
if (dialect === "postgres" || dialect === "postgres-native") {
expect(enumVals.special).to.be.instanceof(Array)
expect(enumVals.special).to.have.length(2);
}
done()
})
})
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
, Sequelize = require(__dirname + '/../index')
, config = require(__dirname + "/config/config")
, moment = require('moment')
chai.Assertion.includeStack = true
var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') {
return '"' + str + '"'
} else if (dialect == 'mysql') {
return '`' + str + '`'
} else {
return str
}
}
describe(Support.getTestDialectTeaser("Sequelize"), function () {
describe('constructor', function() {
it('should pass the global options correctly', function(done) {
var sequelize = Support.createSequelizeInstance({ logging: false, define: { underscored:true } })
, DAO = sequelize.define('dao', {name: DataTypes.STRING})
expect(DAO.options.underscored).to.be.ok
done()
})
it('should correctly set the host and the port', function(done) {
var sequelize = Support.createSequelizeInstance({ host: '127.0.0.1', port: 1234 })
expect(sequelize.config.port).to.equal(1234)
expect(sequelize.config.host).to.equal('127.0.0.1')
done()
})
})
describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).to.be.false
})
it("returns true if the dao was defined before", function() {
this.sequelize.define('Project', {
name: DataTypes.STRING
})
expect(this.sequelize.isDefined('Project')).to.be.true
})
})
describe('query', function() {
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
done()
})
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING
})
this.insertQuery = "INSERT INTO " + qq(this.User.tableName) + " (username, " + qq("createdAt") + ", " + qq("updatedAt") + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"
this.User.sync({ force: true }).success(function() {
done()
})
})
it('executes a query the internal way', function(done) {
this.sequelize.query(this.insertQuery, null, { raw: true })
.complete(function(err, result) {
expect(err).to.be.null
expect(result).to.be.null
done()
})
})
it('executes a query if only the sql is passed', function(done) {
this.sequelize.query(this.insertQuery)
.complete(function(err, result) {
expect(err).to.be.null
expect(result).to.not.exist
done()
})
})
it('executes select queries correctly', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize
.query("select * from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
it('executes select queries correctly when quoteIdentifiers is false', function(done) {
var self = this
, seq = Object.create(self.sequelize)
seq.options.quoteIdentifiers = false
seq.query(this.insertQuery).success(function() {
seq.query("select * from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
it('executes select query and parses dot notation results', function(done) {
var self = this
self.sequelize.query('DELETE FROM ' + qq(self.User.tableName)).complete(function() {
self.sequelize.query(self.insertQuery).success(function() {
self.sequelize
.query("select username as " + qq("user.username") + " from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.user })).to.deep.equal([{'username':'john'}])
done()
})
})
})
})
if (dialect == 'mysql') {
it('executes stored procedures', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() {
self.sequelize.query(
"CREATE PROCEDURE foo()\nSELECT * FROM " + self.User.tableName + ";"
).success(function() {
self.sequelize.query('CALL foo()').success(function(users) {
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
})
})
} else {
console.log('FIXME: I want to be supported in this dialect as well :-(')
}
it('uses the passed DAOFactory', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize.query("SELECT * FROM " + qq(self.User.tableName) + ";", self.User).success(function(users) {
expect(users[0].__factory).to.equal(self.User)
done()
})
})
})
it('destructs dot separated attributes when doing a raw query', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`'
, sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true }).success(function(result) {
expect(result).to.deep.equal([ { foo: { bar: { baz: 1 } } } ])
done()
})
})
it('replaces token with the passed array', function(done) {
this.sequelize.query('select ? as foo, ? as bar', null, { raw: true }, [ 1, 2 ]).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }])
done()
})
})
it('handles AS in conjunction with functions just fine', function(done) {
this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).to.be.true
done()
})
})
})
describe('define', function() {
it("adds a new dao to the dao manager", function(done) {
expect(this.sequelize.daoFactoryManager.all.length).to.equal(0)
this.sequelize.define('foo', { title: DataTypes.STRING })
expect(this.sequelize.daoFactoryManager.all.length).to.equal(1)
done()
})
it("overwrites global options", function(done) {
var sequelize = Support.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: DataTypes.STRING}, {collate: 'utf8_bin'})
expect(DAO.options.collate).to.equal('utf8_bin')
done()
})
it("inherits global collate option", function(done) {
var sequelize = Support.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: DataTypes.STRING})
expect(DAO.options.collate).to.equal('utf8_general_ci')
done()
})
it("inherits global classMethods and instanceMethods", function(done) {
var sequelize = Support.createSequelizeInstance({
define: {
classMethods : { globalClassMethod : function() {} },
instanceMethods : { globalInstanceMethod : function() {} }
}
})
var DAO = sequelize.define('foo', {bar: DataTypes.STRING}, {
classMethods : { localClassMethod : function() {} }
})
expect(typeof DAO.options.classMethods.globalClassMethod).to.equal('function')
expect(typeof DAO.options.classMethods.localClassMethod).to.equal('function')
expect(typeof DAO.options.instanceMethods.globalInstanceMethod).to.equal('function')
done()
})
it("uses the passed tableName", function(done) {
var self = this
, Photo = this.sequelize.define('Foto', { name: DataTypes.STRING }, { tableName: 'photos' })
Photo.sync({ force: true }).success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
expect(tableNames).to.include('photos')
done()
})
})
})
})
describe('sync', function() {
it("synchronizes all daos", function(done) {
var Project = this.sequelize.define('project' + config.rand(), { title: DataTypes.STRING })
var Task = this.sequelize.define('task' + config.rand(), { title: DataTypes.STRING })
Project.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
Project.create({title: 'bla'}).success(function() {
Task.create({title: 'bla'}).success(function(task){
expect(task).to.exist
expect(task.title).to.equal('bla')
done()
})
})
})
})
})
it('works with correct database credentials', function(done) {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
User.sync().success(function() {
expect(true).to.be.true
done()
})
})
it("fails with incorrect database credentials", function(done) {
// sqlite doesn't have a concept of database credentials
if (dialect === "sqlite") {
expect(true).to.be.true
return done()
}
var sequelize2 = Support.getSequelizeInstance('foo', 'bar', null, { logging: false })
, User2 = sequelize2.define('User', { name: DataTypes.STRING, bio: DataTypes.TEXT })
User2.sync().error(function(err) {
if (dialect === "postgres" || dialect === "postgres-native") {
expect(err.message).to.equal('role "bar" does not exist')
} else {
expect(err.message.toString()).to.match(/.*Access\ denied.*/)
}
done()
})
})
})
describe('drop should work', function() {
it('correctly succeeds', function(done) {
var User = this.sequelize.define('Users', {username: DataTypes.STRING })
User.sync({ force: true }).success(function() {
User.drop().success(function() {
expect(true).to.be.true
done()
})
})
})
})
describe('import', function() {
it("imports a dao definition from a file", function(done) {
var Project = this.sequelize.import(__dirname + "/assets/project")
expect(Project).to.exist
done()
})
})
describe('define', function() {
[
{ type: DataTypes.ENUM, values: ['scheduled', 'active', 'finished']},
DataTypes.ENUM('scheduled', 'active', 'finished')
].forEach(function(status) {
describe('enum', function() {
beforeEach(function(done) {
this.Review = this.sequelize.define('review', { status: status })
this.Review.sync({ force: true }).success(function() {
done()
})
})
it('raises an error if no values are defined', function(done) {
var self = this
expect(function() {
self.sequelize.define('omnomnom', {
bla: { type: DataTypes.ENUM }
})
}).to.throw(Error, 'Values for ENUM haven\'t been defined.')
done()
})
it('correctly stores values', function(done) {
this.Review.create({ status: 'active' }).success(function(review) {
expect(review.status).to.equal('active')
done()
})
})
it('correctly loads values', function(done) {
var self = this
this.Review.create({ status: 'active' }).success(function() {
self.Review.findAll().success(function(reviews) {
expect(reviews[0].status).to.equal('active')
done()
})
})
})
it("doesn't save an instance if value is not in the range of enums", function(done) {
var self = this
expect(function() {
self.Review.create({ status: 'fnord' })
}).to.throw(Error, 'Value "fnord" for ENUM status is out of allowed scope. Allowed values: scheduled, active, finished')
done()
})
})
})
describe('table', function() {
[
{ id: { type: DataTypes.BIGINT } },
{ id: { type: DataTypes.STRING, allowNull: true } },
{ id: { type: DataTypes.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].forEach(function(customAttributes) {
it('should be able to override options on the default attributes', function(done) {
var Picture = this.sequelize.define('picture', _.cloneDeep(customAttributes))
Picture.sync({ force: true }).success(function() {
Object.keys(customAttributes).forEach(function(attribute) {
Object.keys(customAttributes[attribute]).forEach(function(option) {
var optionValue = customAttributes[attribute][option];
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue)
})
})
done()
})
})
})
})
})
})
/* jshint camelcase: false */ /* jshint camelcase: false */
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, dbFile = __dirname + '/test.sqlite' , dbFile = __dirname + '/test.sqlite'
, storages = [dbFile] , storages = [dbFile]
, DataTypes = require(__dirname + "/../../lib/data-types")
}
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
describe('[SQLITE] DAOFactory', function() { describe('[SQLITE Specific] DAOFactory', function() {
before(function(done) { after(function(done) {
var self = this this.sequelize.options.storage = ':memory:'
done()
Helpers.initTests({ })
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', { beforeEach(function(done) {
this.sequelize.options.storage = dbFile
this.User = this.sequelize.define('User', {
age: DataTypes.INTEGER, age: DataTypes.INTEGER,
name: DataTypes.STRING, name: DataTypes.STRING,
bio: DataTypes.TEXT bio: DataTypes.TEXT
}) })
}, this.User.sync({ force: true }).success(function() {
onComplete: function() { done()
self.User.sync({ force: true }).success(done)
}
}) })
}) })
storages.forEach(function(storage) { storages.forEach(function(storage) {
describe('with storage "' + storage + '"', function() { describe('with storage "' + storage + '"', function() {
after(function(done) { after(function(done) {
if (storage == dbFile) { if (storage === dbFile) {
require("fs").unlink(__dirname + '/test.sqlite', done) require("fs").writeFile(dbFile, '', function() {
done()
})
} }
}) })
describe('create', function() { describe('create', function() {
it('creates a table entry', function(done) { it('creates a table entry', function(done) {
var self = this var self = this
this.User.create({ age: 21, name: 'John Wayne', bio: 'noot noot' }).success(function(user) { this.User.create({ age: 21, name: 'John Wayne', bio: 'noot noot' }).success(function(user) {
expect(user.age).toEqual(21) expect(user.age).to.equal(21)
expect(user.name).toEqual('John Wayne') expect(user.name).to.equal('John Wayne')
expect(user.bio).toEqual('noot noot') expect(user.bio).to.equal('noot noot')
self.User.all().success(function(users) { self.User.all().success(function(users) {
var usernames = users.map(function(user) { var usernames = users.map(function(user) {
return user.name return user.name
}) })
expect(usernames).toEqual(['John Wayne']) expect(usernames).to.contain('John Wayne')
done() done()
}) })
}) })
...@@ -73,7 +69,7 @@ if (dialect === 'sqlite') { ...@@ -73,7 +69,7 @@ if (dialect === 'sqlite') {
name: 'John Doe', name: 'John Doe',
options: options options: options
}).success(function(people) { }).success(function(people) {
expect(people.options).toEqual(options) expect(people.options).to.deep.equal(options)
done() done()
}) })
}) })
...@@ -90,7 +86,7 @@ if (dialect === 'sqlite') { ...@@ -90,7 +86,7 @@ if (dialect === 'sqlite') {
name: 'John Doe', name: 'John Doe',
has_swag: true has_swag: true
}).success(function(people) { }).success(function(people) {
expect(people.has_swag).toBeTruthy(); expect(people.has_swag).to.be.ok
done() done()
}) })
}) })
...@@ -107,7 +103,7 @@ if (dialect === 'sqlite') { ...@@ -107,7 +103,7 @@ if (dialect === 'sqlite') {
name: 'John Doe', name: 'John Doe',
has_swag: false has_swag: false
}).success(function(people) { }).success(function(people) {
expect(people.has_swag).toBeFalsy(); expect(people.has_swag).to.not.be.ok
done() done()
}) })
}) })
...@@ -115,36 +111,40 @@ if (dialect === 'sqlite') { ...@@ -115,36 +111,40 @@ if (dialect === 'sqlite') {
}) })
describe('.find', function() { describe('.find', function() {
before(function(done) { beforeEach(function(done) {
this.User.create({name: 'user', bio: 'footbar'}).success(done) this.User.create({name: 'user', bio: 'footbar'}).success(function() {
done()
})
}) })
it("finds normal lookups", function(done) { it("finds normal lookups", function(done) {
this.User.find({ where: { name:'user' } }).success(function(user) { this.User.find({ where: { name:'user' } }).success(function(user) {
expect(user.name).toEqual('user') expect(user.name).to.equal('user')
done() done()
}) })
}) })
it("should make aliased attributes available", function(done) { it("should make aliased attributes available", function(done) {
this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) { this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) {
expect(user.username).toEqual('user') expect(user.username).to.equal('user')
done() done()
}) })
}) })
}) })
describe('.all', function() { describe('.all', function() {
before(function(done) { beforeEach(function(done) {
this.User.bulkCreate([ this.User.bulkCreate([
{name: 'user', bio: 'foobar'}, {name: 'user', bio: 'foobar'},
{name: 'user', bio: 'foobar'} {name: 'user', bio: 'foobar'}
]).success(done) ]).success(function() {
done()
})
}) })
it("should return all users", function(done) { it("should return all users", function(done) {
this.User.all().on('success', function(users) { this.User.all().on('success', function(users) {
expect(users.length).toEqual(2) expect(users).to.have.length(2)
done() done()
}) })
}) })
...@@ -161,7 +161,7 @@ if (dialect === 'sqlite') { ...@@ -161,7 +161,7 @@ if (dialect === 'sqlite') {
this.User.bulkCreate(users).success(function() { this.User.bulkCreate(users).success(function() {
self.User.min('age').on('success', function(min) { self.User.min('age').on('success', function(min) {
expect(min).toEqual(2) expect(min).to.equal(2)
done() done()
}) })
}) })
...@@ -179,7 +179,7 @@ if (dialect === 'sqlite') { ...@@ -179,7 +179,7 @@ if (dialect === 'sqlite') {
this.User.bulkCreate(users).success(function() { this.User.bulkCreate(users).success(function() {
self.User.max('age').on('success', function(min) { self.User.max('age').on('success', function(min) {
expect(min).toEqual(5); expect(min).to.equal(5)
done() done()
}) })
}) })
......
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , DataTypes = require(__dirname + "/../../lib/data-types")
} , dialect = Support.getTestDialect()
buster.spec.expose() chai.Assertion.includeStack = true
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
describe('[SQLITE] DAO', function() { describe('[SQLITE Specific] DAO', function() {
before(function(done) { beforeEach(function(done) {
var self = this this.User = this.sequelize.define('User', {
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING username: DataTypes.STRING
}) })
}, this.User.sync({ force: true }).success(function() {
onComplete: function() { done()
self.User.sync({ force: true }).success(done)
}
}) })
}) })
...@@ -32,12 +23,12 @@ if (dialect === 'sqlite') { ...@@ -32,12 +23,12 @@ if (dialect === 'sqlite') {
this.User this.User
.create({ username: 'user', createdAt: new Date(2011, 04, 04) }) .create({ username: 'user', createdAt: new Date(2011, 04, 04) })
.success(function(oldUser) { .success(function() {
self.User.create({ username: 'new user' }).success(function(newUser) { self.User.create({ username: 'new user' }).success(function() {
self.User.findAll({ self.User.findAll({
where: ['createdAt > ?', new Date(2012, 01, 01)] where: ['createdAt > ?', new Date(2012, 01, 01)]
}).success(function(users) { }).success(function(users) {
expect(users.length).toEqual(1) expect(users).to.have.length(1)
done() done()
}) })
}) })
......
if(typeof require === 'function') { var chai = require('chai')
const buster = require("buster") , expect = chai.expect
, Helpers = require('../buster-helpers') , Support = require(__dirname + '/../support')
, dialect = Helpers.getTestDialect() , DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, util = require("util")
, _ = require('lodash')
, moment = require('moment')
, QueryGenerator = require("../../lib/dialects/sqlite/query-generator") , QueryGenerator = require("../../lib/dialects/sqlite/query-generator")
, util = require("util");
}
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
describe('[SQLITE] QueryGenerator', function() { describe('[SQLITE Specific] QueryGenerator', function() {
before(function(done) { beforeEach(function(done) {
var self = this this.User = this.sequelize.define('User', {
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING username: DataTypes.STRING
}) })
}, this.User.sync({ force: true }).success(function() {
onComplete: function() { done()
self.User.sync({ force: true }).success(done)
}
}) })
}) })
...@@ -118,6 +110,9 @@ if (dialect === 'sqlite') { ...@@ -118,6 +110,9 @@ if (dialect === 'sqlite') {
arguments: ['myTable', { name: "bar", value: undefined }], arguments: ['myTable', { name: "bar", value: undefined }],
expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL);" expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL);"
}, { }, {
arguments: ['myTable', {name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}],
expectation: "INSERT INTO `myTable` (`name`,`birthday`) VALUES ('foo','2011-03-27 10:01:55');"
}, {
arguments: ['myTable', { name: "foo", value: true }], arguments: ['myTable', { name: "foo", value: true }],
expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',1);" expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',1);"
}, { }, {
...@@ -149,6 +144,9 @@ if (dialect === 'sqlite') { ...@@ -149,6 +144,9 @@ if (dialect === 'sqlite') {
arguments: ['myTable', [{name: "'bar'"}, {name: 'foo'}]], arguments: ['myTable', [{name: "'bar'"}, {name: 'foo'}]],
expectation: "INSERT INTO `myTable` (`name`) VALUES ('''bar'''),('foo');" expectation: "INSERT INTO `myTable` (`name`) VALUES ('''bar'''),('foo');"
}, { }, {
arguments: ['myTable', [{name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}, {name: 'bar', birthday: moment("2012-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}]],
expectation: "INSERT INTO `myTable` (`name`,`birthday`) VALUES ('foo','2011-03-27 10:01:55'),('bar','2012-03-27 10:01:55');"
}, {
arguments: ['myTable', [{name: "bar", value: null}, {name: 'foo', value: 1}]], arguments: ['myTable', [{name: "bar", value: null}, {name: 'foo', value: 1}]],
expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL),('foo',1);" expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('bar',NULL),('foo',1);"
}, { }, {
...@@ -180,6 +178,12 @@ if (dialect === 'sqlite') { ...@@ -180,6 +178,12 @@ if (dialect === 'sqlite') {
updateQuery: [ updateQuery: [
{ {
arguments: ['myTable', {name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}, {id: 2}],
expectation: "UPDATE `myTable` SET `name`='foo',`birthday`='2011-03-27 10:01:55' WHERE `id`=2"
}, {
arguments: ['myTable', {name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}, 2],
expectation: "UPDATE `myTable` SET `name`='foo',`birthday`='2011-03-27 10:01:55' WHERE `id`=2"
}, {
arguments: ['myTable', { name: 'foo' }, { id: 2 }], arguments: ['myTable', { name: 'foo' }, { id: 2 }],
expectation: "UPDATE `myTable` SET `name`='foo' WHERE `id`=2" expectation: "UPDATE `myTable` SET `name`='foo' WHERE `id`=2"
}, { }, {
...@@ -235,7 +239,7 @@ if (dialect === 'sqlite') { ...@@ -235,7 +239,7 @@ if (dialect === 'sqlite') {
} }
Helpers.Sequelize.Utils._.each(suites, function(tests, suiteTitle) { _.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() { describe(suiteTitle, function() {
tests.forEach(function(test) { tests.forEach(function(test) {
var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments) var title = test.title || 'SQLite correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
...@@ -244,7 +248,7 @@ if (dialect === 'sqlite') { ...@@ -244,7 +248,7 @@ if (dialect === 'sqlite') {
var context = test.context || {options: {}}; var context = test.context || {options: {}};
QueryGenerator.options = context.options QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments) var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation) expect(conditions).to.deep.equal(test.expectation)
done() done()
}) })
}) })
......
File mode changed
const Sequelize = require(__dirname + "/../index") var fs = require('fs')
, 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")
, fs = require('fs')
, buster = require("buster")
var BusterHelpers = module.exports = { var Support = {
Sequelize: Sequelize, Sequelize: Sequelize,
initTests: function(options) { initTests: function(options) {
...@@ -60,6 +59,8 @@ var BusterHelpers = module.exports = { ...@@ -60,6 +59,8 @@ var BusterHelpers = module.exports = {
}, },
getSequelizeInstance: function(db, user, pass, options) { getSequelizeInstance: function(db, user, pass, options) {
options = options || {};
options.dialect = options.dialect || this.getTestDialect()
return new Sequelize(db, user, pass, options) return new Sequelize(db, user, pass, options)
}, },
...@@ -80,6 +81,14 @@ var BusterHelpers = module.exports = { ...@@ -80,6 +81,14 @@ var BusterHelpers = module.exports = {
}) })
}, },
checkMatchForDialects: function(dialect, value, expectations) {
if (!!expectations[dialect]) {
expect(value).to.match(expectations[dialect])
} else {
throw new Error('Undefined expectation for "' + dialect + '"!')
}
},
getTestDialect: function() { getTestDialect: function() {
var envDialect = process.env.DIALECT || 'mysql' var envDialect = process.env.DIALECT || 'mysql'
...@@ -102,22 +111,20 @@ var BusterHelpers = module.exports = { ...@@ -102,22 +111,20 @@ var BusterHelpers = module.exports = {
} }
return "[" + dialect.toUpperCase() + "] " + moduleName return "[" + dialect.toUpperCase() + "] " + moduleName
},
checkMatchForDialects: function(dialect, value, expectations) {
if (!!expectations[dialect]) {
expect(value).toMatch(expectations[dialect])
} else {
throw new Error('Undefined expectation for "' + dialect + '"!')
}
},
assertException: function(block, msg) {
try {
block()
throw new Error('Passed function did not throw an error')
} catch(e) {
buster.assert.equals(e.message, msg)
}
} }
} }
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
before(function(done) {
this.sequelize = sequelize
done()
})
beforeEach(function(done) {
Support.clearDatabase(this.sequelize, function() {
done()
})
})
module.exports = Support
var buster = require("buster") var chai = require('chai')
, Utils = require('../lib/utils') , expect = chai.expect
, Helpers = require('./buster-helpers') , Utils = require(__dirname + '/../lib/utils')
, Support = require(__dirname + '/support')
buster.spec.expose() chai.Assertion.includeStack = true
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("Utils"), function() { describe(Support.getTestDialectTeaser("Utils"), function() {
describe('removeCommentsFromFunctionString', function() { describe('removeCommentsFromFunctionString', function() {
it("removes line comments at the start of a line", function(done) { it("removes line comments at the start of a line", function(done) {
var functionWithLineComments = function() { var functionWithLineComments = function() {
// noot noot // noot noot
} }
var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString()) var string = functionWithLineComments.toString()
expect(result).not.toMatch(/.*noot.*/) , result = Utils.removeCommentsFromFunctionString(string)
expect(result).not.to.match(/.*noot.*/)
done() done()
}) })
...@@ -22,8 +24,10 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -22,8 +24,10 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
alert(1) // noot noot alert(1) // noot noot
} }
var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString()) var string = functionWithLineComments.toString()
expect(result).not.toMatch(/.*noot.*/) , result = Utils.removeCommentsFromFunctionString(string)
expect(result).not.to.match(/.*noot.*/)
done() done()
}) })
...@@ -38,31 +42,32 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -38,31 +42,32 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
}.toString() }.toString()
var result = Utils.removeCommentsFromFunctionString(s) var result = Utils.removeCommentsFromFunctionString(s)
expect(result).not.toMatch(/.*noot.*/)
expect(result).not.toMatch(/.*foo.*/) expect(result).not.to.match(/.*noot.*/)
expect(result).toMatch(/.*alert\(2\).*/) expect(result).not.to.match(/.*foo.*/)
expect(result).to.match(/.*alert\(2\).*/)
done() done()
}) })
}) })
describe('argsArePrimaryKeys', function() { describe('argsArePrimaryKeys', function() {
it("doesn't detect primary keys if primareyKeys and values have different lengths", function(done) { it("doesn't detect primary keys if primareyKeys and values have different lengths", function(done) {
expect(Utils.argsArePrimaryKeys([1,2,3], [1])).toBeFalsy() expect(Utils.argsArePrimaryKeys([1,2,3], [1])).to.be.false
done() done()
}) })
it("doesn't detect primary keys if primary keys are hashes or arrays", function(done) { it("doesn't detect primary keys if primary keys are hashes or arrays", function(done) {
expect(Utils.argsArePrimaryKeys([[]], [1])).toBeFalsy() expect(Utils.argsArePrimaryKeys([[]], [1])).to.be.false
done() done()
}) })
it('detects primary keys if length is correct and data types are matching', function(done) { it('detects primary keys if length is correct and data types are matching', function(done) {
expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).toBeTruthy() expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).to.be.true
done() done()
}) })
it("detects primary keys if primary keys are dates and lengths are matching", function(done) { it("detects primary keys if primary keys are dates and lengths are matching", function(done) {
expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).toBeTruthy() expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).to.be.true
done() done()
}) })
}) })
...@@ -70,34 +75,34 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -70,34 +75,34 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
describe('underscore', function() { describe('underscore', function() {
describe('underscoredIf', function() { describe('underscoredIf', function() {
it('is defined', function(done) { it('is defined', function(done) {
expect(Utils._.underscoredIf).toBeDefined() expect(Utils._.underscoredIf).to.be.ok
done() done()
}) })
it('underscores if second param is true', function(done) { it('underscores if second param is true', function(done) {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar') expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar')
done() done()
}) })
it("doesn't underscore if second param is false", function(done) { it("doesn't underscore if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', false)).toEqual('fooBar') expect(Utils._.underscoredIf('fooBar', false)).to.equal('fooBar')
done() done()
}) })
}) })
describe('camelizeIf', function() { describe('camelizeIf', function() {
it('is defined', function(done) { it('is defined', function(done) {
expect(Utils._.camelizeIf).toBeDefined() expect(Utils._.camelizeIf).to.be.ok
done() done()
}) })
it('camelizes if second param is true', function(done) { it('camelizes if second param is true', function(done) {
expect(Utils._.camelizeIf('foo_bar', true)).toEqual('fooBar') expect(Utils._.camelizeIf('foo_bar', true)).to.equal('fooBar')
done() done()
}) })
it("doesn't camelize if second param is false", function(done) { it("doesn't camelize if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar') expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar')
done() done()
}) })
}) })
...@@ -105,12 +110,12 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -105,12 +110,12 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
describe('isHash', function() { describe('isHash', function() {
it('doesn\'t match arrays', function(done) { it('doesn\'t match arrays', function(done) {
expect(Utils.isHash([])).toBeFalsy() expect(Utils.isHash([])).to.be.false
done() done()
}) })
it('doesn\'t match null', function(done) { it('doesn\'t match null', function(done) {
expect(Utils.isHash(null)).toBeFalsy() expect(Utils.isHash(null)).to.be.false
done() done()
}) })
...@@ -121,7 +126,8 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -121,7 +126,8 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
'last': 'Bar' 'last': 'Bar'
} }
} }
expect(Utils.isHash(values)).toBeTruthy()
expect(Utils.isHash(values)).to.be.true
done() done()
}) })
...@@ -133,7 +139,8 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -133,7 +139,8 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
}, },
'length': 1 'length': 1
} }
expect(Utils.isHash(values)).toBeTruthy()
expect(Utils.isHash(values)).to.be.true
done() done()
}) })
}) })
...@@ -141,13 +148,13 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() { ...@@ -141,13 +148,13 @@ describe(Helpers.getTestDialectTeaser("Utils"), function() {
describe('format', function() { describe('format', function() {
it('should format where clause correctly when the value is truthy', function(done) { it('should format where clause correctly when the value is truthy', function(done) {
var where = ['foo = ?', 1] var where = ['foo = ?', 1]
expect(Utils.format(where)).toEqual('foo = 1') expect(Utils.format(where)).to.equal('foo = 1')
done() done()
}) })
it('should format where clause correctly when the value is falsy', function(done) { it('should format where clause correctly when the value is false', function(done) {
var where = ['foo = ?', 0] var where = ['foo = ?', 0]
expect(Utils.format(where)).toEqual('foo = 0') expect(Utils.format(where)).to.equal('foo = 0')
done() done()
}) })
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!