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

Commit e3b7db2e by Sascha Depold

Merge branch 'master' of github.com:sequelize/sequelize

2 parents 1134d01d 194a42c2
Showing with 2887 additions and 2652 deletions
{ {
"globals": { "globals": {
"jasmine": false,
"spyOn": false, "spyOn": false,
"it": false, "it": false,
"console": false, "console": false,
......
...@@ -4,7 +4,6 @@ before_script: ...@@ -4,7 +4,6 @@ before_script:
script: script:
- "npm run test-buster-travis" - "npm run test-buster-travis"
- "npm run test-jasmine"
notifications: notifications:
email: email:
......
# Sequelize # # Sequelize [![Build Status](https://secure.travis-ci.org/sequelize/sequelize.png)](http://travis-ci.org/sequelize/sequelize) [![Dependency Status](https://david-dm.org/sequelize/sequelize.png)](https://david-dm.org/sequelize/sequelize) [![Dependency Status](https://david-dm.org/sequelize/sequelize.png)](https://david-dm.org/sequelize/sequelize) [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1259407/Sequelize) #
The Sequelize library provides easy access to MySQL, SQLite or PostgreSQL databases by mapping database entries to objects and vice versa. To put it in a nutshell... it's an ORM (Object-Relational-Mapper). The library is written entirely in JavaScript and can be used in the Node.JS environment. MySQL, PostgresSQL, and SQLite Object Relational Mapper (ORM) for [node](http://nodejs.org).
<a href="http://flattr.com/thing/1259407/Sequelize" target="_blank">
<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
## Important Notes ## ## Important Notes ##
...@@ -35,6 +32,7 @@ changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/ ...@@ -35,6 +32,7 @@ changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/
- Asynchronous library - Asynchronous library
- Associations - Associations
- Importing definitions from single files - Importing definitions from single files
- Promises
## Documentation and Updates ## ## Documentation and Updates ##
...@@ -64,7 +62,7 @@ A very basic roadmap. Chances aren't too bad, that not mentioned things are impl ...@@ -64,7 +62,7 @@ A very basic roadmap. Chances aren't too bad, that not mentioned things are impl
- MariaDB support - MariaDB support
- ~~Support for update and delete calls for whole tables without previous loading of instances~~ Implemented in [#569](https://github.com/sequelize/sequelize/pull/569) thanks to @optiltude - ~~Support for update and delete calls for whole tables without previous loading of instances~~ Implemented in [#569](https://github.com/sequelize/sequelize/pull/569) thanks to @optiltude
- Eager loading of nested associations [#388](https://github.com/sdepold/sequelize/issues/388#issuecomment-12019099) - Eager loading of nested associations [#388](https://github.com/sdepold/sequelize/issues/388#issuecomment-12019099)
- Model#delete - ~~Model#delete~~ (renamed to [Model.destroy()](http://sequelizejs.com/documentation#instances-destroy))
- ~~Validate a model before it gets saved.~~ Implemented in [#601](https://github.com/sequelize/sequelize/pull/601), thanks to @durango - ~~Validate a model before it gets saved.~~ Implemented in [#601](https://github.com/sequelize/sequelize/pull/601), thanks to @durango
- Move validation of enum attribute value to validate method - Move validation of enum attribute value to validate method
- BLOB [#99](https://github.com/sequelize/sequelize/issues/99) - BLOB [#99](https://github.com/sequelize/sequelize/issues/99)
...@@ -142,9 +140,7 @@ $ npm install ...@@ -142,9 +140,7 @@ $ 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 `spec` folder (which contains the
lovely [BusterJS](http://busterjs.org) tests) and the `spec-jasmine` folder lovely [BusterJS](http://busterjs.org) tests).
(which contains the ugly and awkward node-jasmine based tests). A main goal
is to get rid of the jasmine 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:
...@@ -153,9 +149,6 @@ to run the test suites for your development environment: ...@@ -153,9 +149,6 @@ to run the test suites for your development environment:
$ # run all tests at once: $ # run all tests at once:
$ npm test $ npm test
$ # run only the jasmine tests (for all dialects):
$ npm run test-jasmine
$ # run all of the buster specs (for all dialects): $ # run all of the buster specs (for all dialects):
$ npm run test-buster $ npm run test-buster
...@@ -167,6 +160,9 @@ $ npm run test-buster-sqlite ...@@ -167,6 +160,9 @@ $ npm run test-buster-sqlite
$ # run the buster specs for postgresql: $ # run the buster specs for postgresql:
$ npm run test-buster-postgres $ npm run test-buster-postgres
$ # alternatively you can pass database credentials with $variables when testing with buster.js
$ DIALECT=dialect SEQ_DB=database SEQ_USER=user SEQ_PW=password buster-test
``` ```
### 5. That's all ### ### 5. That's all ###
...@@ -235,6 +231,17 @@ for (var key in obj) { ...@@ -235,6 +231,17 @@ for (var key in obj) {
```js ```js
{ {
"globals": {
"spyOn": false,
"it": false,
"console": false,
"describe": false,
"expect": false,
"beforeEach": false,
"waits": false,
"waitsFor": false,
"runs": false
},
"camelcase": true, "camelcase": true,
"curly": true, "curly": true,
"forin": true, "forin": true,
...@@ -246,10 +253,3 @@ for (var key in obj) { ...@@ -246,10 +253,3 @@ for (var key in obj) {
"es5": true "es5": true
} }
``` ```
# Build status
The automated tests we talk about just so much are running on
[Travis public CI](http://travis-ci.org), here is its status:
[![Build Status](https://secure.travis-ci.org/sequelize/sequelize.png)](http://travis-ci.org/sequelize/sequelize)
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
- [BUG] Fields should be escaped by quoteIdentifier for max/min functions which allows SQL reserved keywords to be used. [#719](https://github.com/sequelize/sequelize/pull/719). thanks to durango - [BUG] Fields should be escaped by quoteIdentifier for max/min functions which allows SQL reserved keywords to be used. [#719](https://github.com/sequelize/sequelize/pull/719). thanks to durango
- [BUG] Fixed bug when trying to save objects with eagerly loaded attributes [#716](https://github.com/sequelize/sequelize/pull/716). thanks to iamjochen - [BUG] Fixed bug when trying to save objects with eagerly loaded attributes [#716](https://github.com/sequelize/sequelize/pull/716). thanks to iamjochen
- [BUG] Strings for .find() should be fixed. Also added support for string primary keys to be found easily. [#737](https://github.com/sequelize/sequelize/pull/737). thanks to durango - [BUG] Strings for .find() should be fixed. Also added support for string primary keys to be found easily. [#737](https://github.com/sequelize/sequelize/pull/737). 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
- [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
...@@ -39,6 +40,8 @@ ...@@ -39,6 +40,8 @@
- [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen - [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen
- [FEATURE] Made explicit migrations possible. [#728](https://github.com/sequelize/sequelize/pull/728). Thanks to freezy - [FEATURE] Made explicit migrations possible. [#728](https://github.com/sequelize/sequelize/pull/728). Thanks to freezy
- [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 specifying the path to load a module for a dialect. [#766](https://github.com/sequelize/sequelize/pull/766) thanks to sonnym.
- [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
......
...@@ -21,12 +21,26 @@ module.exports = (function() { ...@@ -21,12 +21,26 @@ module.exports = (function() {
where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"} where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"}
if (options.where) { if (options.where) {
if (Array.isArray(options.where)) {
smart = Utils.smartWhere([where, options.where], self.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.__factory.target, smart, self.__factory.target.daoFactoryManager.sequelize.options.dialect)
if (smart.length > 0) {
options.where = smart
}
} else {
Utils._.each(options.where, function(value, index) { Utils._.each(options.where, function(value, index) {
delete options.where[index]; delete options.where[index];
smart = Utils.smartWhere(value, self.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.__factory.target, smart)
if (smart.length > 0) {
value = smart
}
options.where[self.__factory.target.tableName+"."+index] = value; options.where[self.__factory.target.tableName+"."+index] = value;
}); });
Utils._.extend(options.where, where) options.where = Utils._.extend(options.where, where)
}
} else { } else {
options.where = where; options.where = where;
} }
......
...@@ -12,8 +12,17 @@ module.exports = (function() { ...@@ -12,8 +12,17 @@ module.exports = (function() {
where[this.__factory.identifier] = this.instance.id where[this.__factory.identifier] = this.instance.id
options.where = options.where ? Utils._.extend(options.where, where) : where if (options.where) {
return this.__factory.target.findAll(options) smart = Utils.smartWhere([where, options.where], this.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(this.__factory.target, smart)
if (smart.length > 0) {
options.where = smart
}
} else {
options.where = where
}
return this.__factory.target.all(options)
} }
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) { HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
......
...@@ -56,6 +56,12 @@ module.exports = (function() { ...@@ -56,6 +56,12 @@ module.exports = (function() {
params = {where: where} params = {where: where}
} }
smart = Utils.smartWhere([where, params.where || []], self.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.target, smart)
if (smart.length > 0) {
params.where = smart
}
return self.target.find(params) return self.target.find(params)
} }
......
var Toposort = require('toposort-class') var Toposort = require('toposort-class')
, DaoFactory = require('./dao-factory')
module.exports = (function() { module.exports = (function() {
var DAOFactoryManager = function(sequelize) { var DAOFactoryManager = function(sequelize) {
...@@ -43,12 +44,13 @@ module.exports = (function() { ...@@ -43,12 +44,13 @@ module.exports = (function() {
, sorter = new Toposort() , sorter = new Toposort()
this.daos.forEach(function(dao) { this.daos.forEach(function(dao) {
daos[dao.tableName] = dao
var deps = [] var deps = []
for(var attrName in dao.rawAttributes) { daos[dao.tableName] = dao
if(dao.rawAttributes.hasOwnProperty(attrName)) {
if(dao.rawAttributes[attrName].references) { for (var attrName in dao.rawAttributes) {
if (dao.rawAttributes.hasOwnProperty(attrName)) {
if (dao.rawAttributes[attrName].references) {
deps.push(dao.rawAttributes[attrName].references) deps.push(dao.rawAttributes[attrName].references)
} }
} }
......
...@@ -5,8 +5,6 @@ var Utils = require("./utils") ...@@ -5,8 +5,6 @@ var Utils = require("./utils")
module.exports = (function() { module.exports = (function() {
var DAOFactory = function(name, attributes, options) { var DAOFactory = function(name, attributes, options) {
var self = this
this.options = Utils._.extend({ this.options = Utils._.extend({
timestamps: true, timestamps: true,
instanceMethods: {}, instanceMethods: {},
...@@ -24,18 +22,25 @@ module.exports = (function() { ...@@ -24,18 +22,25 @@ module.exports = (function() {
// error check options // error check options
Utils._.each(options.validate, function(validator, validatorType) { Utils._.each(options.validate, function(validator, validatorType) {
if (Utils._.contains(Utils._.keys(attributes), validatorType)) if (Utils._.contains(Utils._.keys(attributes), validatorType)) {
throw new Error("A model validator function must not have the same name as a field. Model: " + name + ", field/validation name: " + validatorType) throw new Error("A model validator function must not have the same name as a field. Model: " + name + ", field/validation name: " + validatorType)
if (!Utils._.isFunction(validator)) }
if (!Utils._.isFunction(validator)) {
throw new Error("Members of the validate option must be functions. Model: " + name + ", error with validate member " + validatorType) throw new Error("Members of the validate option must be functions. Model: " + name + ", error with validate member " + validatorType)
}
}) })
this.name = name this.name = name
if (!this.options.tableName) { if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language) this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language)
} else { } else {
this.tableName = this.options.tableName this.tableName = this.options.tableName
} }
attributes = replaceReferencesWithTableNames(attributes)
this.rawAttributes = attributes this.rawAttributes = attributes
this.daoFactoryManager = null // defined in init function this.daoFactoryManager = null // defined in init function
this.associations = {} this.associations = {}
...@@ -59,8 +64,8 @@ module.exports = (function() { ...@@ -59,8 +64,8 @@ module.exports = (function() {
var self = this; var self = this;
this.daoFactoryManager = daoFactoryManager this.daoFactoryManager = daoFactoryManager
this.primaryKeys = {}; this.primaryKeys = {};
Utils._.each(this.attributes, function(dataTypeString, attributeName) { Utils._.each(this.attributes, function(dataTypeString, attributeName) {
if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) { if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) {
self.primaryKeys[attributeName] = dataTypeString self.primaryKeys[attributeName] = dataTypeString
...@@ -75,9 +80,11 @@ module.exports = (function() { ...@@ -75,9 +80,11 @@ module.exports = (function() {
findAutoIncrementField.call(this) findAutoIncrementField.call(this)
// DAO prototype // DAO prototype
// WTF ... ?
this.DAO = function() { this.DAO = function() {
DAO.apply(this, arguments); DAO.apply(this, arguments);
}; }
Util.inherits(this.DAO, DAO); Util.inherits(this.DAO, DAO);
this.DAO.prototype.rawAttributes = this.rawAttributes; this.DAO.prototype.rawAttributes = this.rawAttributes;
...@@ -89,47 +96,48 @@ module.exports = (function() { ...@@ -89,47 +96,48 @@ module.exports = (function() {
} }
Utils._.each(['Get', 'Set'], function(type) { Utils._.each(['Get', 'Set'], function(type) {
var prop = type.toLowerCase(), var prop = type.toLowerCase()
opt = prop + 'terMethods', , opt = prop + 'terMethods'
meth = '__define' + type + 'ter__', , meth = '__define' + type + 'ter__'
funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {} , funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {}
;
Utils._.each(self.rawAttributes, function(attr, name) { Utils._.each(self.rawAttributes, function(attr, name) {
if (attr.hasOwnProperty(prop)) if (attr.hasOwnProperty(prop)) {
funcs[name] = attr[prop] funcs[name] = attr[prop]
}); }
})
Utils._.each(funcs, function(fct, name) { Utils._.each(funcs, function(fct, name) {
if (!Utils._.isFunction(fct)) if (!Utils._.isFunction(fct)) {
throw new Error(type + 'ter for "' + name + '" is not a function.') throw new Error(type + 'ter for "' + name + '" is not a function.')
}
self.DAO.prototype[meth](name, fct); self.DAO.prototype[meth](name, fct)
}) })
}) })
this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes); this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes);
this.DAO.prototype.booleanValues = []
this.DAO.prototype.defaultValues = {}
this.DAO.prototype.validators = {}
this.DAO.prototype.booleanValues = [];
this.DAO.prototype.defaultValues = {};
this.DAO.prototype.validators = {};
Utils._.each(this.rawAttributes, function (definition, name) { Utils._.each(this.rawAttributes, function (definition, name) {
if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) { if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
self.DAO.prototype.booleanValues.push(name); self.DAO.prototype.booleanValues.push(name);
} }
if (definition.hasOwnProperty('defaultValue')) { if (definition.hasOwnProperty('defaultValue')) {
self.DAO.prototype.defaultValues[name] = function() { self.DAO.prototype.defaultValues[name] = function() {
return Utils.toDefaultValue(definition.defaultValue); return Utils.toDefaultValue(definition.defaultValue)
} }
} }
if (definition.hasOwnProperty('validate')) { if (definition.hasOwnProperty('validate')) {
self.DAO.prototype.validators[name] = definition.validate; self.DAO.prototype.validators[name] = definition.validate;
} }
}); })
this.DAO.prototype.__factory = this; this.DAO.prototype.__factory = this
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues); this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues)
return this return this
} }
...@@ -432,39 +440,26 @@ module.exports = (function() { ...@@ -432,39 +440,26 @@ module.exports = (function() {
, 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'
fields = fields || []
// 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 = []
var found = false
if (fields) {
// Always insert updated and created time stamps
if (self.options.timestamps) {
if (fields.indexOf(updatedAtAttr) === -1) {
fields.push(updatedAtAttr)
}
if (fields.indexOf(createdAtAttr) === -1) {
fields.push(createdAtAttr)
}
}
// Build records for the fields we know about
daos.forEach(function(dao) { daos.forEach(function(dao) {
var values = {}; var values = fields.length > 0 ? {} : dao.dataValues
fields.forEach(function(field) { fields.forEach(function(field) {
values[field] = dao.dataValues[field] values[field] = dao.dataValues[field]
}) })
if (self.options.timestamps) { if (self.options.timestamps) {
values[createdAtAttr] = Utils.now()
values[updatedAtAttr] = Utils.now() values[updatedAtAttr] = Utils.now()
} }
records.push(values);
})
} else { records.push(values);
daos.forEach(function(dao) {
records.push(dao.dataValues)
}) })
}
// Validate enums // Validate enums
records.forEach(function(values) { records.forEach(function(values) {
...@@ -520,6 +515,10 @@ module.exports = (function() { ...@@ -520,6 +515,10 @@ module.exports = (function() {
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where) return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where)
} }
DAOFactory.prototype.describe = function() {
return this.QueryInterface.describeTable(this.tableName)
}
// private // private
var query = function() { var query = function() {
...@@ -627,6 +626,16 @@ module.exports = (function() { ...@@ -627,6 +626,16 @@ module.exports = (function() {
} }
} }
var replaceReferencesWithTableNames = function(attributes) {
Object.keys(attributes).forEach(function(attrName) {
if (attributes[attrName].references instanceof DAOFactory) {
attributes[attrName].references = attributes[attrName].references.tableName
}
})
return attributes
}
Utils._.extend(DAOFactory.prototype, require("./associations/mixin")) Utils._.extend(DAOFactory.prototype, require("./associations/mixin"))
return DAOFactory return DAOFactory
......
...@@ -401,6 +401,20 @@ module.exports = (function() { ...@@ -401,6 +401,20 @@ module.exports = (function() {
for (key in attrs) { for (key in attrs) {
this.addAttribute(key, attrs[key]) this.addAttribute(key, attrs[key])
} }
// this.addAttributes COMPLETELY destroys the structure of our DAO due to __defineGetter__ resetting the object
// so now we have to rebuild for bulkInserts, bulkUpdates, etc.
var rebuild = {}
// Get the correct map....
Utils._.each(this.attributes, function(key) {
if (this.dataValues.hasOwnProperty(key)) {
rebuild[key] = this.dataValues[key]
}
}.bind(this))
// This allows for aliases, etc.
this.dataValues = Utils._.extend(rebuild, this.dataValues)
} }
return DAO return DAO
......
...@@ -4,11 +4,18 @@ var mysql ...@@ -4,11 +4,18 @@ var mysql
, Utils = require("../../utils") , Utils = require("../../utils")
, without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) } , without = function(arr, elem) { return arr.filter(function(e) { return e != elem }) }
try { mysql = require("mysql") } catch (err) {
console.log("You need to install mysql package manually"); }
module.exports = (function() { module.exports = (function() {
var ConnectorManager = function(sequelize, config) { var ConnectorManager = function(sequelize, config) {
try {
if (config.dialectModulePath) {
mysql = require(config.dialectModulePath)
} else {
mysql = require('mysql')
}
} catch (err) {
console.log('You need to install mysql package manually')
}
this.sequelize = sequelize this.sequelize = sequelize
this.client = null this.client = null
this.config = config || {} this.config = config || {}
......
...@@ -536,7 +536,6 @@ module.exports = (function() { ...@@ -536,7 +536,6 @@ module.exports = (function() {
if(dataType.references) { if(dataType.references) {
template += " REFERENCES " + this.quoteIdentifier(dataType.references) template += " REFERENCES " + this.quoteIdentifier(dataType.references)
if(dataType.referencesKey) { if(dataType.referencesKey) {
template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")" template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")"
} else { } else {
......
...@@ -3,12 +3,14 @@ var Query = require("./query") ...@@ -3,12 +3,14 @@ var Query = require("./query")
module.exports = (function() { module.exports = (function() {
var ConnectorManager = function(sequelize, config) { var ConnectorManager = function(sequelize, config) {
var pgModule = config.dialectModulePath || 'pg'
this.sequelize = sequelize this.sequelize = sequelize
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.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg') this.pg = this.config.native ? require(pgModule).native : require(pgModule)
// 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
......
...@@ -289,7 +289,7 @@ module.exports = (function() { ...@@ -289,7 +289,7 @@ module.exports = (function() {
Escape a value (e.g. a string, number or date) Escape a value (e.g. a string, number or date)
*/ */
escape: function(value) { escape: function(value) {
throwMethodUndefined('quoteIdentifier') throwMethodUndefined('escape')
} }
} }
......
var Utils = require("../../utils") var sqlite3
, sqlite3 = require('sqlite3').verbose() , Utils = require("../../utils")
, Query = require("./query") , Query = require("./query")
module.exports = (function() { module.exports = (function() {
var ConnectorManager = function(sequelize) { var ConnectorManager = function(sequelize, config) {
this.sequelize = sequelize this.sequelize = sequelize
if (config.dialectModulePath) {
sqlite3 = require(config.dialectModulePath).verbose()
} else {
sqlite3 = require('sqlite3').verbose()
}
} }
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype) Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
......
...@@ -14,6 +14,7 @@ module.exports = (function() { ...@@ -14,6 +14,7 @@ module.exports = (function() {
@param {String} [password=null] The password which is used to authenticate against the database. @param {String} [password=null] The password which is used to authenticate against the database.
@param {Object} [options={}] An object with options. @param {Object} [options={}] An object with options.
@param {String} [options.dialect='mysql'] The dialect of the relational database. @param {String} [options.dialect='mysql'] The dialect of the relational database.
@param {String} [options.dialectModulePath=null] If specified, load the dialect library from this path.
@param {String} [options.host='localhost'] The host of the relational database. @param {String} [options.host='localhost'] The host of the relational database.
@param {Integer} [options.port=3306] The port of the relational database. @param {Integer} [options.port=3306] The port of the relational database.
@param {String} [options.protocol='tcp'] The protocol of the relational database. @param {String} [options.protocol='tcp'] The protocol of the relational database.
...@@ -68,6 +69,7 @@ module.exports = (function() { ...@@ -68,6 +69,7 @@ module.exports = (function() {
this.options = Utils._.extend({ this.options = Utils._.extend({
dialect: 'mysql', dialect: 'mysql',
dialectModulePath: null,
host: 'localhost', host: 'localhost',
protocol: 'tcp', protocol: 'tcp',
define: {}, define: {},
...@@ -99,6 +101,7 @@ module.exports = (function() { ...@@ -99,6 +101,7 @@ module.exports = (function() {
queue : this.options.queue, queue : this.options.queue,
native : this.options.native, native : this.options.native,
replication: this.options.replication, replication: this.options.replication,
dialectModulePath: this.options.dialectModulePath,
maxConcurrentQueries: this.options.maxConcurrentQueries maxConcurrentQueries: this.options.maxConcurrentQueries
} }
...@@ -188,6 +191,7 @@ module.exports = (function() { ...@@ -188,6 +191,7 @@ module.exports = (function() {
var factory = new DAOFactory(daoName, attributes, options) var factory = new DAOFactory(daoName, attributes, options)
this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager)) this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
return factory return factory
} }
......
...@@ -9,6 +9,19 @@ SqlString.escapeId = function (val, forbidQualified) { ...@@ -9,6 +9,19 @@ SqlString.escapeId = function (val, forbidQualified) {
}; };
SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) { SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
if (arguments.length === 1 && typeof arguments[0] === "object") {
val = val.val || val.value || null
stringifyObjects = val.stringifyObjects || val.objects || undefined
timeZone = val.timeZone || val.zone || null
dialect = val.dialect || null
field = val.field || null
}
else if (arguments.length < 3 && typeof arguments[1] === "object") {
timeZone = stringifyObjects.timeZone || stringifyObjects.zone || null
dialect = stringifyObjects.dialect || null
field = stringifyObjects.field || null
}
if (val === undefined || val === null) { if (val === undefined || val === null) {
return 'NULL'; return 'NULL';
} }
...@@ -19,7 +32,8 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) { ...@@ -19,7 +32,8 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect, field) {
// for us. Postgres actually has a boolean type with true/false literals, // for us. Postgres actually has a boolean type with true/false literals,
// but sequelize doesn't use it yet. // but sequelize doesn't use it yet.
return dialect === 'sqlite' ? +!!val : ('' + !!val); return dialect === 'sqlite' ? +!!val : ('' + !!val);
case 'number': return val+''; case 'number':
return val+'';
} }
if (val instanceof Date) { if (val instanceof Date) {
...@@ -97,7 +111,7 @@ SqlString.dateToString = function(date, timeZone, dialect) { ...@@ -97,7 +111,7 @@ SqlString.dateToString = function(date, timeZone, dialect) {
// TODO: Ideally all dialects would work a bit more like this // TODO: Ideally all dialects would work a bit more like this
if (dialect === "postgres") { if (dialect === "postgres") {
return moment(dt).format("YYYY-MM-DD HH:mm:ss.SSS Z"); return moment(dt).zone('+00:00').format("YYYY-MM-DD HH:mm:ss.SSS Z");
} }
if (timeZone !== 'local') { if (timeZone !== 'local') {
......
var util = require("util") var util = require("util")
, DataTypes = require("./data-types") , DataTypes = require("./data-types")
, SqlString = require("./sql-string") , SqlString = require("./sql-string")
, lodash = require("lodash")
, _string = require('underscore.string')
var Utils = module.exports = { var Utils = module.exports = {
_: (function() { _: (function() {
var _ = require("lodash") var _ = lodash
, _s = require('underscore.string') , _s = _string
_.mixin(_s.exports()) _.mixin(_s.exports())
_.mixin({ _.mixin({
...@@ -39,6 +41,211 @@ var Utils = module.exports = { ...@@ -39,6 +41,211 @@ var Utils = module.exports = {
var timeZone = null; var timeZone = null;
return SqlString.format(arr.shift(), arr, timeZone, dialect) return SqlString.format(arr.shift(), arr, timeZone, dialect)
}, },
// smartWhere can accept an array of {where} objects, or a single {where} object.
// The smartWhere function breaks down the collection of where objects into a more
// centralized object for each column so we can avoid duplicates
// e.g. WHERE username='dan' AND username='dan' becomes WHERE username='dan'
// All of the INs, NOT INs, BETWEENS, etc. are compressed into one key for each column
// This function will hopefully provide more functionality to sequelize in the future.
// tl;dr It's a nice way to dissect a collection of where objects and compress them into one object
smartWhere: function(whereArg, dialect) {
var self = this
, _where = {}
, logic
, type
(Array.isArray(whereArg) ? whereArg : [whereArg]).forEach(function(where) {
// If it's an array we're already good... / it's in a format that can't be broken down further
// e.g. Util.format['SELECT * FROM world WHERE status=?', 'hello']
if (Array.isArray(where)) {
_where._ = where._ || {queries: [], bindings: []}
_where._.queries[_where._.queries.length] = where[0]
if (where.length > 1) {
var values = where.splice(1)
if (dialect === "sqlite") {
values.forEach(function(v, i) {
if (typeof v === "boolean") {
values[i] = (v === true ? 1 : 0)
}
})
}
_where._.bindings = _where._.bindings.concat(values)
}
}
else if (typeof where === "object") {
// First iteration is trying to compress IN and NOT IN as much as possible...
// .. reason being is that WHERE username IN (?) AND username IN (?) != WHERE username IN (?,?)
Object.keys(where).forEach(function(i) {
if (Array.isArray(where[i])) {
where[i] = {
in: where[i]
}
}
})
// Build our smart object
Object.keys(where).forEach(function(i) {
type = typeof where[i]
_where[i] = _where[i] || {}
if (Array.isArray(where[i])) {
_where[i].in = _where[i].in || []
_where[i].in.concat(where[i]);
}
else if (type === "object") {
Object.keys(where[i]).forEach(function(ii) {
logic = self.getWhereLogic(ii)
switch(logic) {
case 'IN':
_where[i].in = _where[i].in || []
_where[i].in = _where[i].in.concat(where[i][ii]);
break
case 'NOT':
_where[i].not = _where[i].not || []
_where[i].not = _where[i].not.concat(where[i][ii]);
break
case 'BETWEEN':
_where[i].between = _where[i].between || []
_where[i].between[_where[i].between.length] = [where[i][ii][0], where[i][ii][1]]
break
case 'NOT BETWEEN':
_where[i].nbetween = _where[i].nbetween || []
_where[i].nbetween[_where[i].nbetween.length] = [where[i][ii][0], where[i][ii][1]]
break
case 'JOIN':
_where[i].joined = _where[i].joined || []
_where[i].joined[_where[i].joined.length] = where[i][ii]
break
default:
_where[i].lazy = _where[i].lazy || {conditions: [], bindings: []}
_where[i].lazy.conditions[_where[i].lazy.conditions.length] = logic + ' ?'
_where[i].lazy.bindings = _where[i].lazy.bindings.concat(where[i][ii])
}
})
}
else if (type === "string" || type === "number" || type === "boolean") {
_where[i].lazy = _where[i].lazy || {conditions: [], bindings: []}
if (type === "boolean") {
_where[i].lazy.conditions[_where[i].lazy.conditions.length] = '= ' + SqlString.escape(where[i], false, null, dialect) // sqlite is special
} else {
_where[i].lazy.conditions[_where[i].lazy.conditions.length] = '= ?'
_where[i].lazy.bindings = _where[i].lazy.bindings.concat(where[i])
}
}
})
}
})
return _where
},
// Converts {smart where} object(s) into an array that's friendly for Utils.format()
// NOTE: Must be applied/called from the QueryInterface
compileSmartWhere: function(obj, dialect) {
var self = this
, whereArgs = []
, text = []
, columnName
if (typeof obj !== "object") {
return obj
}
for (var column in obj) {
if (column === "_") {
text[text.length] = obj[column].queries.join(' AND ')
if (obj[column].bindings.length > 0) {
whereArgs = whereArgs.concat(obj[column].bindings)
}
} else {
Object.keys(obj[column]).forEach(function(condition) {
columnName = self.QueryInterface.quoteIdentifiers(column)
switch(condition) {
case 'in':
text[text.length] = columnName + ' IN (' + obj[column][condition].map(function(){ return '?' }) + ')'
whereArgs = whereArgs.concat(obj[column][condition])
break
case 'not':
text[text.length] = columnName + ' NOT IN (' + obj[column][condition].map(function(){ return '?' }) + ')'
whereArgs = whereArgs.concat(obj[column][condition])
break
case 'between':
Object.keys(obj[column][condition]).forEach(function(row) {
text[text.length] = columnName + ' BETWEEN ? AND ?'
whereArgs = whereArgs.concat(obj[column][condition][row][0], obj[column][condition][row][1])
})
break
case 'nbetween':
Object.keys(obj[column][condition]).forEach(function(row) {
text[text.length] = columnName + ' BETWEEN ? AND ?'
whereArgs = whereArgs.concat(obj[column][condition][row][0], obj[column][condition][row][1])
})
break
case 'joined':
Object.keys(obj[column][condition]).forEach(function(row) {
text[text.length] = columnName + ' = ' + self.QueryInterface.quoteIdentifiers(obj[column][condition][row])
})
break
default: // lazy
text = text.concat(obj[column].lazy.conditions.map(function(val){ return columnName + ' ' + val }))
obj[column].lazy.bindings = obj[column].lazy.bindings.map(function(v) { return SqlString.escape(v, false, null, dialect) })
whereArgs = whereArgs.concat(obj[column].lazy.bindings)
}
})
}
}
return lodash.compact([text.join(' AND ')].concat(whereArgs))
},
getWhereLogic: function(logic) {
switch (logic) {
case 'join':
return 'JOIN'
case 'gte':
return '>='
break
case 'gt':
return '>'
break
case 'lte':
return '<='
break
case 'lt':
return '<'
break
case 'eq':
case 'join':
return '='
break
case 'ne':
return '!='
break
case 'between':
case '..':
return 'BETWEEN'
break
case 'nbetween':
case 'notbetween':
case '!..':
return 'NOT BETWEEN'
break
case 'in':
return 'IN'
break
case 'not':
return 'NOT IN'
break
case 'like':
return 'LIKE'
break
case 'nlike':
case 'notlike':
return 'NOT LIKE'
break
default:
return ''
}
},
isHash: function(obj) { isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj); return Utils._.isObject(obj) && !Array.isArray(obj);
}, },
......
...@@ -32,24 +32,23 @@ ...@@ -32,24 +32,23 @@
"url": "https://github.com/sequelize/sequelize/issues" "url": "https://github.com/sequelize/sequelize/issues"
}, },
"dependencies": { "dependencies": {
"lodash": "~1.2.1", "lodash": "~1.3.1",
"underscore.string": "~2.3.0", "underscore.string": "~2.3.0",
"lingo": "~0.0.5", "lingo": "~0.0.5",
"validator": "1.1.1", "validator": "~1.3.0",
"moment": "~1.7.0", "moment": "~2.1.0",
"commander": "~0.6.0", "commander": "~1.3.0",
"dottie": "0.0.6-1", "dottie": "0.0.8-0",
"toposort-class": "0.1.4", "toposort-class": "~0.2.0",
"generic-pool": "2.0.3", "generic-pool": "2.0.3",
"promise": "~3.0.0" "promise": "~3.2.0"
}, },
"devDependencies": { "devDependencies": {
"jasmine-node": "1.5.0", "sqlite3": "~2.1.12",
"sqlite3": "~2.1.5", "mysql": "~2.0.0-alpha8",
"mysql": "~2.0.0-alpha7", "pg": "~2.1.0",
"pg": "~2.0.0",
"buster": "~0.6.3", "buster": "~0.6.3",
"watchr": "~2.2.0", "watchr": "~2.4.3",
"yuidocjs": "~0.3.36", "yuidocjs": "~0.3.36",
"semver": "~2.0.8" "semver": "~2.0.8"
}, },
...@@ -61,8 +60,7 @@ ...@@ -61,8 +60,7 @@
], ],
"main": "index", "main": "index",
"scripts": { "scripts": {
"test": "npm run test-jasmine && npm run test-buster", "test": "npm run test-buster",
"test-jasmine": "jasmine-node spec-jasmine/",
"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": "npm run test-buster-mysql && npm run test-buster-postgres && npm run test-buster-postgres-native && npm run test-buster-sqlite",
"test-buster-travis": "buster-test", "test-buster-travis": "buster-test",
"test-buster-mysql": "DIALECT=mysql buster-test", "test-buster-mysql": "DIALECT=mysql buster-test",
......
module.exports = {
up: function(migration, DataTypes) {
migration.createTable('Person', {
name: DataTypes.STRING,
isBetaMember: {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
}
})
},
down: function(migration) {
migration.dropTable('Person')
}
}
module.exports = {
up: function() {},
down: function() {}
}
module.exports = {
up: function(migration, DataTypes) {
migration.renameTable('Person', 'User')
},
down: function(migration, DataTypes) {
migration.renameTable('User', 'Person')
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.addColumn('User', 'signature', DataTypes.TEXT)
migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true })
migration.addColumn('User', 'isAdmin', { type: DataTypes.BOOLEAN, defaultValue: false, allowNull: false })
},
down: function(migration, DataTypes) {
migration.removeColumn('User', 'signature')
migration.removeColumn('User', 'shopId')
migration.removeColumn('User', 'isAdmin')
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.removeColumn('User', 'shopId')
},
down: function(migration, DataTypes) {
migration.addColumn('User', 'shopId', { type: DataTypes.INTEGER, allowNull: true })
}
}
module.exports = {
up: function(migration, DataTypes) {
migration.changeColumn('User', 'signature', {
type: DataTypes.STRING,
allowNull: false,
defaultValue: 'Signature'
})
},
down: function(migration, DataTypes) {}
}
module.exports = {
up: function(migration, DataTypes) {
migration.renameColumn('User', 'signature', 'sig')
},
down: function(migration, DataTypes) {
migration.renameColumn('User', 'sig', 'signature')
}
}
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), {
name: DataTypes.STRING
})
}
\ No newline at end of file
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('BelongsTo', function() {
var User = null
, Task = null
var setup = function() {
User = sequelize.define('User', { username: Sequelize.STRING, enabled: {
type: Sequelize.BOOLEAN,
defaultValue: true
}})
Task = sequelize.define('Task', { title: Sequelize.STRING })
}
beforeEach(function() { Helpers.dropAllTables(); setup() })
afterEach(function() { Helpers.dropAllTables() })
it('adds the foreign key', function() {
Task.belongsTo(User)
expect(Task.attributes['UserId']).toEqual("INTEGER")
})
it("underscores the foreign key", function() {
Task = sequelize.define('Task', { title: Sequelize.STRING }, {underscored: true})
Task.belongsTo(User)
expect(Task.attributes['user_id']).toEqual("INTEGER")
})
it("uses the passed foreign key", function() {
Task.belongsTo(User, {foreignKey: 'person_id'})
expect(Task.attributes['person_id']).toEqual("INTEGER")
})
it("defines getters and setters", function() {
Task.belongsTo(User)
var task = Task.build({title: 'asd'})
expect(task.setUser).toBeDefined()
expect(task.getUser).toBeDefined()
})
it("aliases the getters and setters according to the passed 'as' option", function() {
Task.belongsTo(User, {as: 'Person'})
var task = Task.build({title: 'asd'})
expect(task.setPerson).toBeDefined()
expect(task.getPerson).toBeDefined()
})
it("aliases associations to the same table according to the passed 'as' option", function() {
Task.belongsTo(User, {as: 'Poster'})
Task.belongsTo(User, {as: 'Owner'})
var task = Task.build({title: 'asd'})
expect(task.getPoster).toBeDefined()
expect(task.setPoster).toBeDefined()
expect(task.getOwner).toBeDefined()
expect(task.setOwner).toBeDefined()
})
it("intializes the foreign key with null", function() {
Task.belongsTo(User)
var task = Task.build({title: 'asd'})
expect(task['UserId']).not.toBeDefined();
})
it("sets and gets the correct objects", function() {
Task.belongsTo(User, {as: 'User'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'asd'}).success(function(u) {
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() {
Task.belongsTo(User, {as: 'User'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'asd', enabled: false}).success(function(u) {
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() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: 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() {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.belongsTo(Person, {as: 'Mother'})
expect(Person.associations.MotherPersons.options.foreignKey).toEqual('MotherId')
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('HasMany', function() {
var User = null
, Task = null
, sequelize = null
, Helpers = null
var setup = function() {
sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
Helpers = new (require("../config/helpers"))(sequelize)
Helpers.dropAllTables()
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING })
}
beforeEach(function() { setup() })
afterEach(function() { Helpers.dropAllTables() })
describe('mono-directional', function() {
it("adds the foreign key", function() {
User.hasMany(Task)
expect(Task.attributes.UserId).toEqual("INTEGER")
})
it('adds the foreign key with underscore', function() {
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING }, { underscored: true })
Task.hasMany(User)
expect(User.attributes.task_id).toBeDefined()
})
it('uses the passed foreign key', function() {
User.hasMany(Task, { foreignKey: 'person_id' })
expect(Task.attributes.person_id).toEqual("INTEGER")
})
it('defines getters and setters', function() {
User.hasMany(Task)
var u = User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
})
it("defines getters and setters according to the 'as' option", function() {
User.hasMany(Task, {as: 'Tasks'})
var u = User.build({username: 'asd'})
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
})
it("sets and gets associated objects", function() {
var user, task1, task2;
User.hasMany(Task, { as: 'Tasks' })
Helpers.async(function(done) {
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'name'}).success(function(_user) {
Task.create({title: 'task1'}).success(function(_task1) {
Task.create({title: 'task2'}).success(function(_task2) {
user = _user
task1 = _task1
task2 = _task2
done()
})
})
})
})
Helpers.async(function(done) {
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).toEqual(null)
done()
})
})
})
})
})
it("should allow selfAssociation to be single linked (only one DAO is created)", function() {
var oldLength = sequelize.daoFactoryManager.daos.length;
var Comment = sequelize.define('Comment', { content: Sequelize.STRING })
Comment.belongsTo(Comment, {as: "Parent"});
Comment.hasMany(Comment, {as: 'Children', foreignKey: "ParentId", useJunctionTable: false})
expect(sequelize.daoFactoryManager.daos.length).toEqual(oldLength + 1)
Helpers.async(function(done) {
Comment.sync({force: true}).success(function() {
done()
})
})
var parent
Helpers.async(function(done) {
Comment.create({ content: 'parentComment' }).success(function(p) {
parent = p
done()
})
})
Helpers.async(function(done) {
Comment.create({ content: 'child1' }).success(function(child1) {
child1.setParent(parent).success(function() {
done()
})
})
})
Helpers.async(function(done) {
Comment.create({ content: 'child2' }).success(function(child2) {
child2.setParent(parent).success(function() {
done()
})
})
})
Helpers.async(function(done) {
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() {
Helpers.async(function(done) {
var oldLength = sequelize.daoFactoryManager.daos.length;
var Comment = sequelize.define('Comment', { content: Sequelize.STRING })
Comment.belongsTo(Comment, {as: "Parent"})
Comment.hasMany(Comment, {as: 'Children'})
expect(sequelize.daoFactoryManager.daos.length).toEqual(oldLength + 2)
done();
})
})
})
describe('bi-directional', function() {
it('adds the foreign key', function() {
Task.hasMany(User)
User.hasMany(Task)
expect(Task.attributes.UserId).toBeUndefined()
expect(User.attributes.UserId).toBeUndefined()
var daos = sequelize.daoFactoryManager.daos.filter(function(dao) {
return (dao.tableName == (Task.tableName + User.tableName))
})
daos.forEach(function(dao) {
expect(dao.attributes.UserId).toBeDefined()
expect(dao.attributes.TaskId).toBeDefined()
})
})
it("adds the foreign key with underscores", function() {
User = sequelize.define('User', { username: Sequelize.STRING }, { underscored: true })
Task = sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
User.hasMany(Task)
expect(Task.attributes.user_id).toBeUndefined()
expect(User.attributes.user_id).toBeUndefined()
var daos = 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()
})
})
it("uses the passed foreign keys", function() {
User.hasMany(Task, { foreignKey: 'person_id' })
Task.hasMany(User, { foreignKey: 'work_item_id' })
var daos = sequelize.daoFactoryManager.daos.filter(function(dao) {
return (dao.tableName == (Task.tableName + User.tableName))
})
daos.forEach(function(dao) {
expect(dao.attributes.person_id).toBeDefined()
expect(dao.attributes.work_item_id).toBeDefined()
})
})
it("defines getters and setters", function() {
User.hasMany(Task)
Task.hasMany(User)
var u = User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = Task.build({ title: 'foobar' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
})
it("defines getters and setters according to the 'as' option", function() {
User.hasMany(Task, { as: 'Tasks' })
Task.hasMany(User, { as: 'Users' })
var u = User.build({ username: 'asd' })
expect(u.setTasks).toBeDefined()
expect(u.getTasks).toBeDefined()
var t = Task.build({ title: 'asd' })
expect(t.setUsers).toBeDefined()
expect(t.getUsers).toBeDefined()
})
it("sets and gets the corrected associated objects", function() {
var users = []
, tasks = []
User.hasMany(Task, {as: 'Tasks'})
Task.hasMany(User, {as: 'Users'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(done)
})
})
Helpers.async(function(done) {
User.create({username: 'name'}).success(function(user1) {
User.create({username: 'name2'}).success(function(user2) {
Task.create({title: 'task1'}).success(function(task1) {
Task.create({title: 'task2'}).success(function(task2) {
users.push(user1)
users.push(user2)
tasks.push(task1)
tasks.push(task2)
done()
})
})
})
})
})
Helpers.async(function(done) {
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() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: 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 = 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() {
Helpers.async(function(done) {
var Child = sequelize.define('Child', { name: Sequelize.STRING }, {underscore: true, freezeTableName: true})
var Parent = sequelize.define('Parent', { name: Sequelize.STRING }, {underscore: true, freezeTableName: true})
var ParentJoin = sequelize.define('ParentRelationship', { parent_id: Sequelize.INTEGER, child_id: Sequelize.INTEGER }, {underscore: true, freezeTableName: true})
Parent.hasMany(Child, {as: 'Children', foreignKey: 'child_id', joinTableName: 'ParentRelationship'})
Child.hasMany(Parent, {as: 'Parents', foreignKey: 'parent_id', joinTableName: 'ParentRelationship'})
var parents = []
ParentJoin.sync({force: true}).success(function() {
Parent.sync({force: true}).success(function() {
Child.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() {
var User = sequelize.define('User', { name: Sequelize.STRING }, {underscore: true, freezeTableName: true})
var Company = sequelize.define('Company', { name: Sequelize.STRING }, {underscore: true, freezeTableName: true})
var CompanyAccess = sequelize.define('CompanyAccess', { company_id: Sequelize.INTEGER, user_id: Sequelize.INTEGER, permission: Sequelize.STRING }, {underscore: true, freezeTableName: true})
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'})
Helpers.async(function(done) {
var companies = []
CompanyAccess.sync({force: true}).success(function() {
User.sync({force: true}).success(function() {
Company.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() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: 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()
})
})
})
})
})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('HasOne', function() {
var User = null
, Task = null
var setup = function() {
User = sequelize.define('User', { username: Sequelize.STRING })
Task = sequelize.define('Task', { title: Sequelize.STRING })
}
beforeEach(function() { Helpers.dropAllTables(); setup() })
afterEach(function() { Helpers.dropAllTables() })
it("adds the foreign key", function() {
User.hasOne(Task)
expect(Task.attributes.UserId).toEqual("INTEGER")
})
it("adds an underscored foreign key", function() {
User = sequelize.define('User', { username: Sequelize.STRING }, {underscored: true})
Task = sequelize.define('Task', { title: Sequelize.STRING })
User.hasOne(Task)
expect(Task.attributes.user_id).toEqual("INTEGER")
})
it("uses the passed foreign key", function() {
User = sequelize.define('User', { username: Sequelize.STRING }, {underscored: true})
Task = sequelize.define('Task', { title: Sequelize.STRING })
User.hasOne(Task, {foreignKey: 'person_id'})
expect(Task.attributes.person_id).toEqual("INTEGER")
})
it("defines the getter and the setter", function() {
User.hasOne(Task)
var u = User.build({username: 'asd'})
expect(u.setTask).toBeDefined()
expect(u.getTask).toBeDefined()
})
it("defined the getter and the setter according to the passed 'as' option", function() {
User.hasOne(Task, {as: 'Work'})
var u = User.build({username: 'asd'})
expect(u.setWork).toBeDefined()
expect(u.getWork).toBeDefined()
})
it("aliases associations to the same table according to the passed 'as' option", function() {
User.hasOne(Task, {as: 'Work'});
User.hasOne(Task, {as: 'Play'});
var u = User.build({username: 'asd'})
expect(u.getWork).toBeDefined()
expect(u.setWork).toBeDefined()
expect(u.getPlay).toBeDefined()
expect(u.setPlay).toBeDefined()
})
it("gets and sets the correct objects", function() {
var user, task;
User.hasOne(Task, {as: 'Task'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(function() {
User.create({username: 'name'}).success(function(_user) {
Task.create({title: 'snafu'}).success(function(_task) {
user = _user
task = _task
done()
})
})
})
})
})
Helpers.async(function(done) {
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).toEqual(null)
done()
})
})
})
})
})
it("unsets unassociated objects", function() {
var user, task1, task2;
User.hasOne(Task, {as: 'Task'})
Helpers.async(function(done) {
User.sync({force: true}).success(function() {
Task.sync({force: true}).success(function() {
User.create({username: 'name'}).success(function(_user) {
Task.create({title: 'snafu'}).success(function(_task1) {
Task.create({title: 'another task'}).success(function(_task2) {
user = _user
task1 = _task1
task2 = _task2
done()
})
})
})
})
})
})
Helpers.async(function(done) {
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() {
Helpers.async(function(done) {
var Person = sequelize.define('Person', { name: 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() {
var Person = sequelize.define('Person', { name: Sequelize.STRING })
Person.hasOne(Person, {as: 'Mother'})
expect(Person.associations.MotherPersons.options.foreignKey).toEqual('MotherId')
})
})
module.exports = {
rand: function() {
return parseInt(Math.random() * 999)
},
//make maxIdleTime small so that tests exit promptly
mysql: {
username: "root",
password: null,
database: 'sequelize_test',
host: '127.0.0.1',
port: 3306,
pool: { maxConnections: 5, maxIdleTime: 30}
},
sqlite: {
},
postgres: {
database: 'sequelize_test',
username: "postgres",
port: 5432,
pool: { maxConnections: 5, maxIdleTime: 30}
}
}
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 })
}
var config = require("./config/config")
, Sequelize = require("../index")
, dialects = ['sqlite', 'mysql', 'postgres']
describe('DAOFactory', function() {
dialects.forEach(function(dialect) {
describe('with dialect "' + dialect + '"', function() {
var User = null
, sequelize = new Sequelize(
config[dialect].database,
config[dialect].username,
config[dialect].password,
{
logging: false,
dialect: dialect,
port: config[dialect].port
}
)
, Helpers = new (require("./config/helpers"))(sequelize)
var setup = function(options) {
User = sequelize.define('User', options || {
age: Sequelize.INTEGER,
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
Helpers.dropAllTables()
Helpers.async(function(done) {
User.sync({force: true}).success(done).error(function(err) { console.log(err) })
})
}
var checkMatchForDialects = function(value, expectations) {
if(!!expectations[dialect])
expect(value).toMatch(expectations[dialect])
else
throw new Error('Undefined expectation for "' + dialect + '"!')
}
beforeEach(function() { setup() })
afterEach(function() { Helpers.dropAllTables() })
describe('destroy', function() {
it('deletes a record from the database if dao is not paranoid', function() {
Helpers.async(function(done) {
User = sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
User.sync({force: true}).success(done)
})
Helpers.async(function(done) {
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()
}).error(function(err) { console.log(err) })
}).error(function(err) { console.log(err) })
}).error(function(err) { console.log(err) })
})
})
})
it('allows sql logging of delete statements', function() {
Helpers.async(function(done) {
User = sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
User.sync({force: true}).success(done)
})
Helpers.async(function(done) {
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()
}).error(function(err) { console.log(err) })
}).error(function(err) { console.log(err) })
})
})
})
it('allows sql logging of update statements', function() {
Helpers.async(function(done) {
User = sequelize.define('User', {
name: Sequelize.STRING, bio: Sequelize.TEXT
}, { paranoid:true })
User.sync({ force: true }).success(done)
})
Helpers.async(function(done) {
User.create({ name: 'meg', bio: 'none' }).success(function(u) {
expect(u).toBeDefined()
expect(u).not.toBe(null)
u.destroy().on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("UPDATE")).toBeGreaterThan(-1)
done()
})
})
})
})
})
describe('findAll', function() {
var users = []
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, function(_users) {
users = _users
}, 2)
})
it("finds all entries", function() {
Helpers.async(function(done) {
User.findAll().on('success', function(_users) {
expect(_users.length).toEqual(2)
done()
})
})
})
it("finds all users matching the passed conditions", function() {
Helpers.async(function(done) {
User.findAll({where: "id != " + users[1].id}).success(function(_users) {
expect(_users.length).toEqual(1)
done()
})
})
})
it("can also handle array notation", function() {
Helpers.async(function(done){
User.findAll({where: ['id = ?', users[1].id]}).success(function(_users) {
expect(_users.length).toEqual(1)
expect(_users[0].id).toEqual(users[1].id)
done()
})
})
})
it("sorts the results via id", function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, function(_users) {
users = _users
}, 2)
Helpers.async(function(done) {
setTimeout(function() {
User.create({name: 'user', bio: 'foobar'}).success(function(user) {
users.push(user)
done()
})
}, 2000)
})
Helpers.async(function(done) {
User.findAll({ order: "id DESC" }).success(function(users) {
expect(users[0].id).toBeGreaterThan(users[2].id)
done()
})
})
})
it("sorts the results via createdAt", function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, function(_users) {
users = _users
}, 2)
Helpers.async(function(done) {
setTimeout(function() {
User.create({name: 'user', bio: 'foobar'}).success(function(user) {
users.push(user)
done()
})
}, 2000)
})
Helpers.async(function(done) {
User.findAll({ order: 'createdAt DESC' }).success(function(users) {
expect(users[0].id).toBeGreaterThan(users[2].id)
done()
})
})
})
it("handles offset and limit", function() {
setup()
Helpers.Factories.User({name: 'user', bio: 'foobar'}, null, 10)
Helpers.async(function(done) {
User.findAll({ limit: 2, offset: 2 }).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].id).toEqual(3)
done()
})
})
})
})
describe('findAndCountAll', function() {
var users = [], fullcount = 3
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, function(_users) {
users = _users
}, fullcount)
})
it("handles where clause [only]", function() {
Helpers.async(function(done) {
User.findAndCountAll({where: "id != " + users[0].id}).success(function(info) {
expect(info.count).toEqual(fullcount - 1)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
it("handles where clause with ordering [only]", function() {
Helpers.async(function(done) {
User.findAndCountAll({where: "id != " + users[0].id, order: 'id ASC'}).success(function(info) {
expect(info.count).toEqual(fullcount - 1)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
it("handles offset", function() {
Helpers.async(function(done) {
User.findAndCountAll({offset: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(fullcount - 1)
done()
})
})
})
it("handles limit", function() {
Helpers.async(function(done) {
User.findAndCountAll({limit: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
})
it("handles offset and limit", function() {
Helpers.async(function(done) {
User.findAndCountAll({offset: 1, limit: 1}).success(function(info) {
expect(info.count).toEqual(fullcount)
expect(Array.isArray(info.rows)).toBeTruthy()
expect(info.rows.length).toEqual(1)
done()
})
})
})
})
describe('all', function() {
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, null, 2)
})
it("should return all users", function() {
Helpers.async(function(done) {
User.all().on('success', function(users) {
done()
expect(users.length).toEqual(2)
}).on('error', function(err) { console.log(err) })
})
})
})
describe('count', function() {
it('counts all created objects', function() {
Helpers.async(function(done) {
User.create({name: 'user1'}).success(function() {
User.create({name: 'user2'}).success(done)
})
})
Helpers.async(function(done) {
User.count().success(function(count) {
expect(count).toEqual(2)
done()
})
})
})
it('allows sql logging', function() {
Helpers.async(function(done) {
User.count()
.on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
})
it('filters object', function() {
Helpers.async(function(done) {
User.create({name: 'user1'}).success(function() {
User.create({name: 'foo'}).success(done)
})
})
Helpers.async(function(done) {
User.count({where: "name LIKE '%us%'"}).success(function(count) {
expect(count).toEqual(1)
done()
})
})
})
})
describe('equals', function() {
it("correctly determines equality of objects", function() {
setup({ name: Sequelize.STRING, bio: Sequelize.TEXT })
Helpers.async(function(done) {
User.create({name: 'hallo', bio: '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() {
setup({
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING, bio: Sequelize.TEXT
})
Helpers.async(function(done) {
User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equals(u)).toBeTruthy()
done()
}).error(function(err) { console.log(err) })
})
})
}
})
describe('equalsOneOf', function() {
// sqlite can't handle multiple primary keys
if(dialect != 'sqlite') {
beforeEach(function() {
setup({
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING, bio: Sequelize.TEXT
})
})
it('determines equality if one is matching', function() {
Helpers.async(function(done) {
User.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() {
Helpers.async(function(done) {
User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([{b:2}, {a:1}])).toBeFalsy()
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('sync', function() {
it('works with correct database credentials', function() {
Helpers.async(function(done) {
User.sync().success(done)
})
})
it("fails with incorrect database credentials", function() {
Helpers.async(function(done) {
var sequelize2 = new Sequelize('foo', 'bar', null, { logging: false })
, User2 = sequelize2.define('User', { name: Sequelize.STRING, bio: Sequelize.TEXT })
User2.sync().error(function(err) {
expect(err.message).toMatch(/.*Access\ denied.*/)
done()
})
})
})
})
describe('drop should work', function() {
it('correctly succeeds', function() {
Helpers.async(function(done) {
User.drop().success(done)
})
})
})
})
})
})
var config = require("./config/config")
, Sequelize = require("../index")
, dialects = ['sqlite', 'mysql', 'postgres']
describe('DAO', function() {
dialects.forEach(function(dialect) {
describe('with dialect "' + dialect + '"', function() {
var User = null
, sequelize = new Sequelize(
config[dialect].database,
config[dialect].username,
config[dialect].password,
{
logging: false,
dialect: dialect,
port: config[dialect].port,
host: config[dialect].host
}
)
, Helpers = new (require("./config/helpers"))(sequelize)
var setup = function() {
Helpers.async(function(done) {
User = sequelize.define('User', {
username: Sequelize.STRING,
birthDate: Sequelize.DATE
})
User.sync({ force: true }).success(done)
})
}
beforeEach(function() { Helpers.dropAllTables(); setup() })
afterEach(function() { Helpers.dropAllTables() })
describe('Escaping', function() {
it('is done properly for special characters', function() {
var User = sequelize.define('User', {
bio: Sequelize.TEXT
}, { timestamps: false, logging: false })
Helpers.async(function(done) {
User.sync({ force: true }).success(done)
})
Helpers.async(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
User.create({ bio: bio }).success(function(u1) {
User.find(u1.id).success(function(u2) {
expect(u2.bio).toEqual(bio)
done()
})
})
})
})
})
describe('isNewRecord', function() {
it('returns true for non-saved objects', function() {
var user = User.build({ username: 'user' })
expect(user.id).toBeNull()
expect(user.isNewRecord).toBeTruthy()
})
it("returns false for saved objects", function() {
Helpers.async(function(done) {
User.build({ username: 'user' }).save().success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
})
it("returns false for created objects", function() {
Helpers.async(function(done) {
User.create({ username: 'user' }).success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
})
it("returns false for objects found by find method", function() {
Helpers.async(function(done) {
User.create({ username: 'user' }).success(function(user) {
User.create({ username: 'user' }).success(function(user) {
User.find(user.id).success(function(user) {
expect(user.isNewRecord).toBeFalsy()
done()
})
})
})
})
})
it("returns false for objects found by findAll method", function() {
var chainer = new Sequelize.Utils.QueryChainer
for(var i = 0; i < 10; i++)
chainer.add(User.create({ username: 'user' }))
Helpers.async(function(done) {
chainer.run().success(function() {
User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isNewRecord).toBeFalsy()
})
done()
})
})
})
})
})
describe('save', function() {
it('only updates fields in passed array', function() {
var user = null
, user2 = null
, userId = null
, date = new Date(1990, 01, 01)
Helpers.async(function(done) {
User.create({
username: 'foo',
birthDate: new Date()
}).success(function(_user) {
user = _user
done()
}).error(function(err) {
console.log(err)
})
})
Helpers.async(function(done) {
user.username = 'fizz'
user.birthDate = date
done()
})
Helpers.async(function(done) {
user.save(['username']).success(function(){
// re-select user
User.find(user.id).success(function(_user2) {
user2 = _user2
done()
})
})
})
Helpers.async(function(done) {
// name should have changed
expect(user2.username).toEqual('fizz')
// bio should be unchanged
expect(user2.birthDate).toNotEqual(date)
done()
})
})
it("stores an entry in the database", function() {
var username = 'user'
, user = User.build({
username: username,
birthDate: new Date(1984, 8, 23)
})
Helpers.async(function(done) {
User.all().success(function(users) {
expect(users.length).toEqual(0)
done()
})
})
Helpers.async(function(done) {
user.save().success(done)
})
Helpers.async(function(done) {
User.all().success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual(username)
expect(users[0].birthDate instanceof Date).toBe(true)
expect(users[0].birthDate).toEqual(new Date(1984, 8, 23))
done()
})
})
})
it("updates the timestamps", function() {
var now = Date.now()
, user = null
, updatedAt = null
Helpers.async(function(done) {
// timeout is needed, in order to check the update of the timestamp
setTimeout(function() {
user = User.build({ username: 'user' })
updatedAt = user.updatedAt
expect(updatedAt.getTime()).toBeGreaterThan(now)
done()
}, 1000)
})
Helpers.async(function(done) {
setTimeout(function() {
user.save().success(function() {
expect(updatedAt.getTime()).toBeLessThan(user.updatedAt.getTime())
done()
})
}, 1000)
})
})
describe('without timestamps option', function() {
var User2 = sequelize.define('User2', {
username: Sequelize.STRING,
updatedAt: Sequelize.DATE
}, {
timestamps: false
})
beforeEach(function() {
Helpers.async(function(done) {
User2.sync({ force: true }).success(done)
})
})
it("doesn't update the updatedAt column", function() {
Helpers.async(function(done) {
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()
})
})
})
})
})
describe('updateAttributes', function() {
it("updates attributes in the database", function() {
Helpers.async(function(done) {
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() {
Helpers.async(function(done) {
User.create({ username: 'user' }).success(function(user) {
user.updateAttributes({ username: 'person', foo: 'bar'}).success(function(user) {
expect(user.username).toEqual('person')
expect(user.foo).toBeUndefined()
done()
})
})
})
})
it("doesn't update primary keys or timestamps", function() {
var User = sequelize.define('User' + config.rand(), {
name: Sequelize.STRING, bio: Sequelize.TEXT, identifier: {type: Sequelize.STRING, primaryKey: true}
})
Helpers.async(function(done) {
User.sync({ force: true }).success(done)
})
Helpers.async(function(done) {
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() {
var User = sequelize.define('User' + config.rand(), {
name: Sequelize.STRING, bio: Sequelize.TEXT, identifier: {type: Sequelize.STRING, primaryKey: true}
})
Helpers.async(function(done) {
User.sync({ force:true }).success(done)
})
Helpers.async(function(done) {
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()
})
})
})
})
})
describe('values', function() {
it('returns all values', function() {
var User = sequelize.define('User', {
username: Sequelize.STRING
}, { timestamps: false, logging: false })
Helpers.async(function(done) {
User.sync({ force: true }).success(done)
})
Helpers.async(function(done) {
var user = User.build({ username: 'foo' })
expect(user.values).toEqual({ username: "foo", id: null })
done()
})
})
})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('HasMany', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
//prevent periods from occurring in the table name since they are used to delimit (table.column)
var User = sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: Sequelize.STRING })
, Task = sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Sequelize.STRING })
, users = null
, tasks = null
User.hasMany(Task, {as:'Tasks'})
Task.hasMany(User, {as:'Users'})
beforeEach(function() {
Helpers.async(function(_done) {
Helpers.Factories.DAO(User.name, {name: 'User' + Math.random()}, function(_users) {
users = _users; _done()
}, 5)
})
Helpers.async(function(_done) {
Helpers.Factories.DAO(Task.name, {name: 'Task' + Math.random()}, function(_tasks) {
tasks = _tasks; _done()
}, 2)
})
})
describe('addDAO / getDAO', function() {
var user = null
, task = null
beforeEach(function() {
Helpers.async(function(done) {
User.all().on('success', function(_users) {
Task.all().on('success', function(_tasks) {
user = _users[0]
task = _tasks[0]
done()
})
})
})
})
it('should correctly add an association to the dao', function() {
Helpers.async(function(done) {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
user.addTask(task).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
done()
})
})
})
})
})
})
describe('removeDAO', function() {
var user = null
, tasks = null
beforeEach(function() {
Helpers.async(function(done) {
User.all().on('success', function(users) {
Task.all().on('success', function(_tasks) {
user = users[0]
tasks = _tasks
done()
})
})
})
})
it("should correctly remove associated objects", function() {
Helpers.async(function(done) {
user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
user.setTasks(tasks).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(tasks.length)
user.removeTask(tasks[0]).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('Associations', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
/////////// many-to-many with same prefix ////////////
describe('many-to-many', function() {
describe('where tables have the same prefix', function() {
var Table2 = sequelize.define('wp_table2', {foo: Sequelize.STRING})
, Table1 = sequelize.define('wp_table1', {foo: Sequelize.STRING})
Table1.hasMany(Table2)
Table2.hasMany(Table1)
it("should create a table wp_table1wp_table2s", function() {
Helpers.async(function(done) {
expect(sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
})
})
})
describe('when join table name is specified', function() {
var Table2 = sequelize.define('ms_table1', {foo: Sequelize.STRING})
, Table1 = sequelize.define('ms_table2', {foo: Sequelize.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
it("should not use a combined name", function() {
expect(sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).toBeUndefined()
})
it("should use the specified name", function() {
expect(sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('ConnectorManager', function() {
beforeEach(function() {
Helpers.dropAllTables()
})
afterEach(function() {
Helpers.dropAllTables()
})
it('works correctly after being idle', function() {
var User = sequelize.define('User', { username: Sequelize.STRING })
Helpers.async(function(done) {
User.sync({force: true}).on('success', function() {
User.create({username: 'user1'}).on('success', function() {
User.count().on('success', function(count) {
expect(count).toEqual(1)
done()
})
})
})
})
Helpers.async(function(done) {
setTimeout(function() {
User.count().on('success', function(count) {
expect(count).toEqual(1)
done()
})
}, 1000)
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port })
, Helpers = new (require("../config/helpers"))(sequelize)
describe('DAOFactory', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
var User = sequelize.define('User', { age: Sequelize.INTEGER, name: Sequelize.STRING, bio: Sequelize.TEXT })
describe('constructor', function() {
it("handles extended attributes (unique)", function() {
var User = sequelize.define('User' + config.rand(), {
username: { type: Sequelize.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
})
it("handles extended attributes (default)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: Sequelize.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
})
it("handles extended attributes (null)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: Sequelize.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
})
it("handles extended attributes (comment)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: 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"})
})
it("handles extended attributes (primaryKey)", function() {
var User = sequelize.define('User' + config.rand(), {
username: {type: Sequelize.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) PRIMARY KEY"})
})
it("adds timestamps", function() {
var User1 = sequelize.define('User' + config.rand(), {})
var User2 = 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"})
})
it("adds deletedAt if paranoid", function() {
var User = 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"})
})
it("underscores timestamps if underscored", function() {
var User = 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"})
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function() {
var User = sequelize.define('User' + config.rand(), {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: Sequelize.STRING
})
expect(User.primaryKeys).toEqual({"foo":"VARCHAR(255) PRIMARY KEY"})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.postgres.database, config.postgres.username, config.postgres.password, {
logging: false,
port: config.postgres.port,
dialect: 'postgres'
})
, Helpers = new (require("../config/helpers"))(sequelize)
describe('HasMany', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
//prevent periods from occurring in the table name since they are used to delimit (table.column)
var User = sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: Sequelize.STRING })
, Task = sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Sequelize.STRING })
, users = null
, tasks = null
User.hasMany(Task, {as:'Tasks'})
Task.hasMany(User, {as:'Users'})
beforeEach(function() {
Helpers.async(function(_done) {
Helpers.Factories.DAO(User.name, {name: 'User' + Math.random()}, function(_users) {
users = _users; _done()
}, 5)
})
Helpers.async(function(_done) {
Helpers.Factories.DAO(Task.name, {name: 'Task' + Math.random()}, function(_tasks) {
tasks = _tasks; _done()
}, 2)
})
})
describe('addDAO / getDAO', function() {
var user = null
, task = null
beforeEach(function() {
Helpers.async(function(done) {
User.all().on('success', function(_users) {
Task.all().on('success', function(_tasks) {
user = _users[0]
task = _tasks[0]
done()
})
})
})
})
it('should correctly add an association to the dao', function() {
Helpers.async(function(done) {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
user.addTask(task).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
done()
})
})
})
})
})
})
describe('removeDAO', function() {
var user = null
, tasks = null
beforeEach(function() {
Helpers.async(function(done) {
User.all().on('success', function(users) {
Task.all().on('success', function(_tasks) {
user = users[0]
tasks = _tasks
done()
})
})
})
})
it("should correctly remove associated objects", function() {
Helpers.async(function(done) {
user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
user.setTasks(tasks).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(tasks.length)
user.removeTask(tasks[0]).on('success', function() {
user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.postgres.database, config.postgres.username, config.postgres.password, {
logging: false,
port: config.postgres.port,
dialect: 'postgres'
})
, Helpers = new (require("../config/helpers"))(sequelize)
describe('Associations', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
/////////// many-to-many with same prefix ////////////
describe('many-to-many', function() {
describe('where tables have the same prefix', function() {
var Table2 = sequelize.define('wp_table2', {foo: Sequelize.STRING})
, Table1 = sequelize.define('wp_table1', {foo: Sequelize.STRING})
Table1.hasMany(Table2)
Table2.hasMany(Table1)
it("should create a table wp_table1wp_table2s", function() {
Helpers.async(function(done) {
expect(sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
})
})
})
describe('when join table name is specified', function() {
var Table2 = sequelize.define('ms_table1', {foo: Sequelize.STRING})
, Table1 = sequelize.define('ms_table2', {foo: Sequelize.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
it("should not use a combined name", function() {
expect(sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).toBeUndefined()
})
it("should use the specified name", function() {
expect(sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
})
})
})
})
var config = require("./config/config")
, Sequelize = require("../index")
, QueryInterface = require("../lib/query-interface")
describe('Sequelize', function() {
var sequelize = null
, Helpers = null
var setup = function(options) {
options = options || {}
if (!options.hasOwnProperty('pool'))
options.pool = config.mysql.pool
if (!options.hasOwnProperty('logging'))
options.logging = false
if (!options.hasOwnProperty('host'))
options.host = config.mysql.host
if (!options.hasOwnProperty('port'))
options.port = config.mysql.port
sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, options)
Helpers = new (require("./config/helpers"))(sequelize)
return options
}
beforeEach(function() { setup() })
afterEach(function() { sequelize = null })
describe('constructor', function() {
it('should pass the global options correctly', function() {
setup({ logging: false, define: { underscored:true } })
var DAO = sequelize.define('dao', {name: Sequelize.STRING})
expect(DAO.options.underscored).toBeTruthy()
})
it('should correctly set the host and the port', function() {
var options = setup({ host: '127.0.0.1', port: 1234 })
expect(sequelize.config.host).toEqual(options.host)
expect(sequelize.config.port).toEqual(options.port)
})
})
describe('define', function() {
it("adds a new dao to the dao manager", function() {
expect(sequelize.daoFactoryManager.all.length).toEqual(0)
sequelize.define('foo', { title: Sequelize.STRING })
expect(sequelize.daoFactoryManager.all.length).toEqual(1)
})
it("overwrites global options", function() {
setup({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Sequelize.STRING}, {collate: 'utf8_bin'})
expect(DAO.options.collate).toEqual('utf8_bin')
})
it("inherits global collate option", function() {
setup({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: Sequelize.STRING})
expect(DAO.options.collate).toEqual('utf8_general_ci')
})
it("inherits global classMethods and instanceMethods", function() {
setup({
define: {
classMethods : { globalClassMethod : function() {} },
instanceMethods : { globalInstanceMethod : function() {} }
}
})
var DAO = sequelize.define('foo', {bar: 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')
})
it("uses the passed tableName", function(done) {
var Photo = sequelize.define('Foto', { name: Sequelize.STRING }, { tableName: 'photos' })
Photo.sync({ force: true }).success(function() {
sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
expect(tableNames).toContain('photos')
done()
})
})
})
})
describe('sync', function() {
it("synchronizes all daos", function() {
var Project = sequelize.define('project' + config.rand(), { title: Sequelize.STRING })
var Task = sequelize.define('task' + config.rand(), { title: Sequelize.STRING })
Helpers.async(function(done) {
sequelize.sync().success(function() {
Project.create({title: 'bla'}).success(function() {
Task.create({title: 'bla'}).success(done)
})
})
})
})
})
describe('import', function() {
it("imports a dao definition from a file", function() {
var Project = sequelize.import(__dirname + "/assets/project")
expect(Project).toBeDefined()
})
})
})
var config = require("../config/config")
, Sequelize = require("../../index")
, dbFile = __dirname + '/test.sqlite'
, storages = [':memory:', dbFile]
describe('DAOFactory', function() {
storages.forEach(function(storage) {
describe('with storage "' + storage + '"', function() {
var User = null
, sequelize = null
, Helpers = null
beforeEach(function() {
sequelize = new Sequelize(config.database, config.username, config.password, {
logging: false,
dialect: 'sqlite',
storage: storage
})
Helpers = new (require("../config/helpers"))(sequelize)
User = sequelize.define('User', {
age: Sequelize.INTEGER,
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
Helpers.sync()
})
afterEach(function() {
Helpers.dropAllTables()
if(storage == dbFile) {
Helpers.async(function(done) {
require("fs").unlink(__dirname + '/test.sqlite', done)
})
}
})
describe('create', function() {
it('creates a table entry', function() {
Helpers.async(function(done) {
User
.create({ age: 21, name: 'John Wayne', bio: 'noot noot' })
.success(done)
.error(function(err) { console.log(err) })
})
Helpers.async(function(done) {
User.all().success(function(users) {
var usernames = users.map(function(user) {
return user.name
})
expect(usernames).toEqual(['John Wayne'])
done()
}).error(function(err){ console.log(err) })
})
})
it('should allow the creation of an object with options as attribute', function() {
var Person = sequelize.define('Person', {
name: Sequelize.STRING,
options: Sequelize.TEXT
})
Helpers.async(function(done) {
Person.sync({force: true}).success(done)
})
Helpers.async(function(done) {
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
Helpers.Factories.DAO('Person', {
name: 'John Doe',
options: options
}, function(people) {
expect(people[0].options).toEqual(options)
done()
})
})
})
it('should allow the creation of an object with a boolean (true) as attribute', function() {
var Person = sequelize.define('Person', {
name: Sequelize.STRING,
has_swag: Sequelize.BOOLEAN
})
Helpers.async(function(done) {
Person.sync({force: true}).success(done)
})
Helpers.async(function(done) {
Helpers.Factories.DAO('Person', {
name: 'John Doe',
has_swag: true
}, function(people) {
expect(people[0].has_swag).toBeTruthy();
done()
})
})
})
it('should allow the creation of an object with a boolean (false) as attribute', function() {
var Person = sequelize.define('Person', {
name: Sequelize.STRING,
has_swag: Sequelize.BOOLEAN
})
Helpers.async(function(done) {
Person.sync({force: true}).success(done)
})
Helpers.async(function(done) {
Helpers.Factories.DAO('Person', {
name: 'John Doe',
has_swag: false
}, function(people) {
expect(people[0].has_swag).toBeFalsy();
done()
})
})
})
})
////////// find //////////////
describe('.find', function() {
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, null, 2)
})
it("finds normal lookups", function() {
Helpers.async(function(done) {
User.find({ where: { name:'user' } }).success(function(user) {
expect(user.name).toEqual('user')
done()
})
})
})
it("should make aliased attributes available", function() {
Helpers.async(function(done) {
User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) {
expect(user.username).toEqual('user')
done()
})
})
})
})
////////// all //////////////
describe('.all', function() {
beforeEach(function() {
Helpers.Factories.User({name: 'user', bio: 'foobar'}, null, 2)
})
it("should return all users", function() {
Helpers.async(function(done) {
User.all().on('success', function(users) {
done()
expect(users.length).toEqual(2)
}).on('error', function(err) { console.log(err) })
})
})
})
////////// min //////////////
describe('.min', function() {
it("should return the min value", function() {
for(var i = 2; i < 5; i++) Helpers.Factories.User({ age: i })
Helpers.async(function(done) {
User.min('age').on('success', function(min) {
expect(min).toEqual(2); done()
})
})
})
})
////////// max //////////////
describe('.max', function() {
it("should return the max value", function() {
for(var i = 2; i <= 5; i++) Helpers.Factories.User({ age: i })
Helpers.async(function(done) {
User.max('age').on('success', function(min) {
expect(min).toEqual(5); done()
})
})
})
})
})
})
})
// var config = require("./config/config")
// , Sequelize = require("../index")
// , User = null
// , sequelize = new Sequelize(config.database, config.username, config.password, {
// logging: false,
// dialect: dialect
// })
// , Helpers = new (require("./config/helpers"))(sequelize)
// describe('DAO', function() {
// var setup = function() {
// Helpers.async(function(done) {
// User = sequelize.define('User', { username: Sequelize.STRING })
// User.sync({ force: true }).success(done)
// })
// }
// beforeEach(function() { Helpers.dropAllTables(); setup() })
// afterEach(function() { Helpers.dropAllTables() })
// describe('findAll', function() {
// it("can handle dates correctly", function() {
// })
// })
// })
/* jshint camelcase: false */
if (typeof require === 'function') { if (typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, Helpers = require('../buster-helpers') , Helpers = require('../buster-helpers')
...@@ -19,6 +20,137 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() { ...@@ -19,6 +20,137 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
}) })
}) })
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() { describe('setAssociation', function() {
it('clears the association if null is passed', function(done) { it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING }) var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
...@@ -49,7 +181,6 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() { ...@@ -49,7 +181,6 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING }) , User = this.sequelize.define('User', { username: Sequelize.STRING })
...@@ -176,7 +307,6 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() { ...@@ -176,7 +307,6 @@ describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
}) })
}) })
}) })
}) })
describe("Association options", function() { describe("Association options", function() {
......
/* jshint camelcase: false */
if (typeof require === 'function') { if (typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, Helpers = require('../buster-helpers') , Helpers = require('../buster-helpers')
, Sequelize = require('../../index') , Sequelize = require('../../index')
, dialect = Helpers.getTestDialect() , dialect = Helpers.getTestDialect()
, _ = require('lodash') , _ = require('lodash')
, moment = require('moment')
} }
buster.spec.expose() buster.spec.expose()
buster.testRunner.timeout = 500 buster.testRunner.timeout = 500
describe(Helpers.getTestDialectTeaser("HasMany"), function() { describe(Helpers.getTestDialectTeaser("HasMany"), function() {
before(function(done) { before(function(done) {
var self = this var self = this
...@@ -21,11 +22,359 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -21,11 +22,359 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
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('(1:N)', function() {
describe('hasSingle', function() { describe('hasSingle', function() {
before(function(done) { before(function(done) {
this.Article = this.sequelize.define('Article', { 'title': Sequelize.STRING }) this.Article = this.sequelize.define('Article', {
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING }) 'title': Sequelize.STRING
})
this.Label = this.sequelize.define('Label', {
'text': Sequelize.STRING
})
this.Article.hasMany(this.Label) this.Article.hasMany(this.Label)
...@@ -67,7 +416,8 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -67,7 +416,8 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
[ article, 'hasLabel', [ label2 ]] [ article, 'hasLabel', [ label2 ]]
]) ])
chainer.runSerially().success(function(_, label1, hasLabel1, hasLabel2) { chainer.runSerially()
.success(function(_, label1, hasLabel1, hasLabel2) {
expect(hasLabel1).toBeTrue() expect(hasLabel1).toBeTrue()
expect(hasLabel2).toBeFalse() expect(hasLabel2).toBeFalse()
done() done()
...@@ -78,8 +428,12 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -78,8 +428,12 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
describe('hasAll', function() { describe('hasAll', function() {
before(function(done) { before(function(done) {
this.Article = this.sequelize.define('Article', { 'title': Sequelize.STRING }) this.Article = this.sequelize.define('Article', {
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING }) 'title': Sequelize.STRING
})
this.Label = this.sequelize.define('Label', {
'text': Sequelize.STRING
})
this.Article.hasMany(this.Label) this.Article.hasMany(this.Label)
...@@ -199,6 +553,38 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -199,6 +553,38 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
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) { it("gets all associated objects when no options are passed", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) { this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) { john.getTasks().success(function (tasks) {
...@@ -309,6 +695,15 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -309,6 +695,15 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
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() { it("removes the reference id, which was added in the first place", function() {
...@@ -385,7 +780,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -385,7 +780,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.Task.create({ title: 'task2' }).success(function(task2) { this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("INSERT INTO") expect(sql).toMatch("INSERT INTO")
expect(sql).toMatch("VALUES (1,1),(1,2)") expect(sql).toMatch("VALUES (1,1),(2,1)")
})).success(function () { })).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into
done() done()
......
/* jshint camelcase: false */
if (typeof require === 'function') { if (typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, Sequelize = require("../../index") , Sequelize = require("../../index")
...@@ -19,6 +20,157 @@ describe(Helpers.getTestDialectTeaser("HasOne"), function() { ...@@ -19,6 +20,157 @@ describe(Helpers.getTestDialectTeaser("HasOne"), function() {
}) })
}) })
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() { describe('setAssociation', function() {
it('clears the association if null is passed', function(done) { it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING }) var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
...@@ -49,7 +201,6 @@ describe(Helpers.getTestDialectTeaser("HasOne"), function() { ...@@ -49,7 +201,6 @@ describe(Helpers.getTestDialectTeaser("HasOne"), function() {
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING }) , User = this.sequelize.define('User', { username: Sequelize.STRING })
......
...@@ -18,6 +18,16 @@ describe(Helpers.getTestDialectTeaser("Mixin"), function() { ...@@ -18,6 +18,16 @@ describe(Helpers.getTestDialectTeaser("Mixin"), function() {
}) })
}) })
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() { describe('getAssociation', function() {
it('returns the respective part of the association for 1:1 associations', function() { it('returns the respective part of the association for 1:1 associations', function() {
var User = this.sequelize.define('User', {}) var User = this.sequelize.define('User', {})
...@@ -28,5 +38,62 @@ describe(Helpers.getTestDialectTeaser("Mixin"), function() { ...@@ -28,5 +38,62 @@ describe(Helpers.getTestDialectTeaser("Mixin"), function() {
expect(User.getAssociation(Task).target).toEqual(Task) 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)
})
})
})
})
})
})
})
})
}) })
}) })
...@@ -30,18 +30,28 @@ var BusterHelpers = module.exports = { ...@@ -30,18 +30,28 @@ var BusterHelpers = module.exports = {
options.dialect = options.dialect || 'mysql' options.dialect = options.dialect || 'mysql'
options.logging = (options.hasOwnProperty('logging') ? options.logging : false) options.logging = (options.hasOwnProperty('logging') ? options.logging : false)
options.pool = options.pool || config.pool
var sequelizeOptions = { var sequelizeOptions = {
logging: options.logging, logging: options.logging,
dialect: options.dialect, dialect: options.dialect,
port: process.env.SEQ_PORT || config[options.dialect].port port: options.port || process.env.SEQ_PORT || config[options.dialect].port,
pool: options.pool
}
if (!!options.host) {
sequelizeOptions.host = options.host
}
if (!!options.define) {
sequelizeOptions.define = options.define
} }
if (process.env.DIALECT === 'postgres-native') { if (process.env.DIALECT === 'postgres-native') {
sequelizeOptions.native = true sequelizeOptions.native = true
} }
return new Sequelize( return this.getSequelizeInstance(
process.env.SEQ_DB || config[options.dialect].database, process.env.SEQ_DB || config[options.dialect].database,
process.env.SEQ_USER || process.env.SEQ_USERNAME || config[options.dialect].username, process.env.SEQ_USER || process.env.SEQ_USERNAME || config[options.dialect].username,
process.env.SEQ_PW || process.env.SEQ_PASSWORD || config[options.dialect].password, process.env.SEQ_PW || process.env.SEQ_PASSWORD || config[options.dialect].password,
...@@ -49,6 +59,10 @@ var BusterHelpers = module.exports = { ...@@ -49,6 +59,10 @@ var BusterHelpers = module.exports = {
) )
}, },
getSequelizeInstance: function(db, user, pass, options) {
return new Sequelize(db, user, pass, options)
},
clearDatabase: function(sequelize, callback) { clearDatabase: function(sequelize, callback) {
sequelize sequelize
.getQueryInterface() .getQueryInterface()
......
...@@ -3,6 +3,7 @@ if(typeof require === 'function') { ...@@ -3,6 +3,7 @@ if(typeof require === 'function') {
, Sequelize = require("../index") , Sequelize = require("../index")
, Helpers = require('./buster-helpers') , Helpers = require('./buster-helpers')
, _ = require('lodash') , _ = require('lodash')
, moment = require('moment')
, dialect = Helpers.getTestDialect() , dialect = Helpers.getTestDialect()
} }
...@@ -576,6 +577,23 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -576,6 +577,23 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
describe('bulkCreate', function() { 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) { it('inserts multiple values respecting the white list', function(done) {
var self = this var self = this
...@@ -725,6 +743,26 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -725,6 +743,26 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) // - bulkCreate }) // - bulkCreate
describe('update', function() { 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) { it('updates only values that match filter', function(done) {
var self = this var self = this
...@@ -782,6 +820,45 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -782,6 +820,45 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) // - update }) // - update
describe('destroy', function() { 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) { it('deletes values that match filter', function(done) {
var self = this var self = this
...@@ -790,14 +867,11 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -790,14 +867,11 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
{ username: 'Bob', secretValue: '43' }] { username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() { this.User.bulkCreate(data).success(function() {
self.User.destroy({secretValue: '42'}) self.User.destroy({secretValue: '42'})
.success(function() { .success(function() {
self.User.findAll({order: 'id'}).success(function(users) { self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(1) expect(users.length).toEqual(1)
expect(users[0].username).toEqual("Bob") expect(users[0].username).toEqual("Bob")
done() done()
}) })
}) })
...@@ -805,7 +879,6 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -805,7 +879,6 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
it('sets deletedAt to the current timestamp if paranoid is true', function(done) { it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this var self = this
, User = this.sequelize.define('ParanoidUser', { , User = this.sequelize.define('ParanoidUser', {
username: Sequelize.STRING, username: Sequelize.STRING,
...@@ -820,9 +893,7 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -820,9 +893,7 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
{ username: 'Bob', secretValue: '43' }] { username: 'Bob', secretValue: '43' }]
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
User.bulkCreate(data).success(function() { User.bulkCreate(data).success(function() {
User.destroy({secretValue: '42'}) User.destroy({secretValue: '42'})
.success(function() { .success(function() {
User.findAll({order: 'id'}).success(function(users) { User.findAll({order: 'id'}).success(function(users) {
...@@ -839,11 +910,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -839,11 +910,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
}) })
}) })
}) })
}) })
}) // - destroy }) // - destroy
describe('special where conditions', function() { describe('special where conditions', function() {
...@@ -1843,8 +1911,248 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -1843,8 +1911,248 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
}) // - describe: queryOptions }) // - 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: 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() { describe('min', function() {
before(function(done) { before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', { this.UserWithAge = this.sequelize.define('UserWithAge', {
...@@ -2039,4 +2347,124 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -2039,4 +2347,124 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}.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
}) })
...@@ -2,6 +2,8 @@ if (typeof require === 'function') { ...@@ -2,6 +2,8 @@ if (typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, Helpers = require('./buster-helpers') , Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect() , dialect = Helpers.getTestDialect()
, Sequelize = require("../index")
, config = require(__dirname + "/config/config")
, _ = require('lodash') , _ = require('lodash')
} }
...@@ -39,6 +41,13 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -39,6 +41,13 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
} }
}) })
self.User2 = sequelize.define('User2', {
username: DataTypes.STRING,
updatedAt: DataTypes.DATE
}, {
timestamps: false
})
self.HistoryLog = sequelize.define('HistoryLog', { self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING }, someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER }, aNumber: { type: DataTypes.INTEGER },
...@@ -63,6 +72,74 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -63,6 +72,74 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
}) })
}) })
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 () { describe('increment', function () {
before(function (done) { before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done) this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
...@@ -74,7 +151,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -74,7 +151,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.increment(['aNumber'], 2).done(function (err, user2) { user1.increment(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2); expect(user3.aNumber).toBe(2);
done(); done();
...@@ -89,7 +165,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -89,7 +165,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.increment('aNumber', 2).done(function (err, user2) { user1.increment('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2); expect(user3.aNumber).toBe(2);
done(); done();
...@@ -108,7 +183,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -108,7 +183,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
aNumber: user2.aNumber + 1 aNumber: user2.aNumber + 1
}).done(function (err, user3) { }).done(function (err, user3) {
user1.increment(['aNumber'], 2).done(function (err, user4) { user1.increment(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) { self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(3); expect(user5.aNumber).toBe(3);
done(); done();
...@@ -142,7 +216,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -142,7 +216,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.increment({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) { user1.increment({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(1); expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2); expect(user3.bNumber).toBe(2);
...@@ -164,7 +237,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -164,7 +237,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.decrement(['aNumber'], 2).done(function (err, user2) { user1.decrement(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2); expect(user3.aNumber).toBe(-2);
done(); done();
...@@ -179,7 +251,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -179,7 +251,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.decrement('aNumber', 2).done(function (err, user2) { user1.decrement('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2); expect(user3.aNumber).toBe(-2);
done(); done();
...@@ -198,7 +269,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -198,7 +269,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
aNumber: user2.aNumber + 1 aNumber: user2.aNumber + 1
}).done(function (err, user3) { }).done(function (err, user3) {
user1.decrement(['aNumber'], 2).done(function (err, user4) { user1.decrement(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) { self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(-1); expect(user5.aNumber).toBe(-1);
done(); done();
...@@ -232,7 +302,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -232,7 +302,6 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
// Select something // Select something
this.User.find(1).done(function (err, user1) { this.User.find(1).done(function (err, user1) {
user1.decrement({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) { user1.decrement({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) { self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-1); expect(user3.aNumber).toBe(-1);
expect(user3.bNumber).toBe(-2); expect(user3.bNumber).toBe(-2);
...@@ -391,6 +460,86 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -391,6 +460,86 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
}) })
describe('save', function() { 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){ it('should fail a validation upon creating', function(done){
this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){ this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){
expect(err).toBeDefined() expect(err).toBeDefined()
...@@ -780,7 +929,90 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() { ...@@ -780,7 +929,90 @@ describe(Helpers.getTestDialectTeaser("DAO"), function() {
}) })
}) })
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() { 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) { it('stores and restores null values', function(done) {
var Download = this.sequelize.define('download', { var Download = this.sequelize.define('download', {
startedAt: Helpers.Sequelize.DATE, startedAt: Helpers.Sequelize.DATE,
......
/* 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] 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)
}
})
})
describe('many-to-many', function() {
describe('where tables have the same prefix', function() {
it("should create a table wp_table1wp_table2s", function(done) {
var Table2 = this.sequelize.define('wp_table2', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING})
, self = this
Table1.hasMany(Table2)
Table2.hasMany(Table1)
this.sequelize.sync({ force: true }).success(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
})
})
})
describe('when join table name is specified', function() {
before(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
this.sequelize.sync({ force: true }).success(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) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
done()
})
})
})
describe('HasMany', function() {
before(function(done) {
//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.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
var self = this
, users = []
, tasks = []
for (var i = 0; i < 5; ++i) {
users[users.length] = {name: 'User' + Math.random()}
}
for (var x = 0; x < 5; ++x) {
tasks[tasks.length] = {name: 'Task' + Math.random()}
}
this.sequelize.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(done)
})
})
})
describe('addDAO / getDAO', function() {
before(function(done) {
var self = this
self.user = null
self.task = null
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.task = _tasks[0]
done()
})
})
})
it('should correctly add an association to the dao', function(done) {
var self = this
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
done()
})
})
})
})
})
describe('removeDAO', function() {
before(function(done) {
var self = this
self.user = null
self.tasks = null
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.tasks = _tasks
done()
})
})
})
it("should correctly remove associated objects", function(done) {
var self = this
self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] Connector Manager', 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)
}
})
})
it('works correctly after being idle', function(done) {
this.timeout = 1000 * 10
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
, spy = this.spy()
User.sync({force: true}).on('success', function() {
User.create({username: 'user1'}).on('success', function() {
User.count().on('success', function(count) {
expect(count).toEqual(1)
spy()
setTimeout(function() {
User.count().on('success', function(count) {
expect(count).toEqual(1)
spy()
if (spy.calledTwice) {
done()
}
})
}, 1000)
})
})
})
})
})
}
/* 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()
})
})
})
}
var config = require("../config/config") /* jshint camelcase: false */
, Sequelize = require("../../index") if(typeof require === 'function') {
, sequelize = new Sequelize(config.mysql.database, config.mysql.username, config.mysql.password, { pool: config.mysql.pool, logging: false, host: config.mysql.host, port: config.mysql.port }) const buster = require("buster")
, Helpers = new (require("../config/helpers"))(sequelize) , config = require('../config/config')
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/mysql/query-generator") , QueryGenerator = require("../../lib/dialects/mysql/query-generator")
, util = require("util") , util = require("util")
describe('QueryGenerator', function() { }
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
var suites = { buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] 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 = {
attributesToSQL: [ attributesToSQL: [
{ {
arguments: [{id: 'INTEGER'}], arguments: [{id: 'INTEGER'}],
...@@ -407,19 +425,20 @@ describe('QueryGenerator', function() { ...@@ -407,19 +425,20 @@ describe('QueryGenerator', function() {
] ]
} }
Sequelize.Utils._.each(suites, function(tests, suiteTitle) { Helpers.Sequelize.Utils._.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)
it(title, function() { it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
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).toEqual(test.expectation)
done()
})
}) })
}) })
}) })
}) })
}) }
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^postgres/)) {
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)
}
})
})
describe('many-to-many', function() {
describe('where tables have the same prefix', function() {
it("should create a table wp_table1wp_table2s", function(done) {
var Table2 = this.sequelize.define('wp_table2', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING})
Table1.hasMany(Table2)
Table2.hasMany(Table1)
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
})
})
describe('when join table name is specified', function() {
before(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
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) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
done()
})
})
})
describe('HasMany', function() {
before(function(done) {
//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.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
var self = this
, users = []
, tasks = []
for (var i = 0; i < 5; ++i) {
users[users.length] = {name: 'User' + Math.random()}
}
for (var x = 0; x < 5; ++x) {
tasks[tasks.length] = {name: 'Task' + Math.random()}
}
this.sequelize.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(done)
})
})
})
describe('addDAO / getDAO', function() {
before(function(done) {
var self = this
self.user = null
self.task = null
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.task = _tasks[0]
done()
})
})
})
it('should correctly add an association to the dao', function(done) {
var self = this
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
done()
})
})
})
})
})
describe('removeDAO', function() {
before(function(done) {
var self = this
self.user = null
self.tasks = null
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.tasks = _tasks
done()
})
})
})
it("should correctly remove associated objects", function(done) {
var self = this
self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
}
var config = require("../config/config") if(typeof require === 'function') {
, Sequelize = require("../../index") const buster = require("buster")
, sequelize = new Sequelize(config.postgres.database, config.postgres.username, config.postgres.password, { , Helpers = require('../buster-helpers')
logging: false, , dialect = Helpers.getTestDialect()
dialect: 'postgres',
port: config.postgres.port
})
, Helpers = new (require("../config/helpers"))(sequelize)
, QueryGenerator = require("../../lib/dialects/postgres/query-generator") , QueryGenerator = require("../../lib/dialects/postgres/query-generator")
, util = require("util") , util = require("util")
, moment = require('moment')
}
describe('QueryGenerator', function() { buster.spec.expose()
beforeEach(function() { Helpers.sync() }) buster.testRunner.timeout = 1000
afterEach(function() { Helpers.drop() })
var suites = { if (dialect.match(/^postgres/)) {
describe('[POSTGRES] QueryGenerator', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
})
})
var suites = {
attributesToSQL: [ attributesToSQL: [
{ {
arguments: [{id: 'INTEGER'}], arguments: [{id: 'INTEGER'}],
...@@ -337,7 +355,7 @@ describe('QueryGenerator', function() { ...@@ -337,7 +355,7 @@ describe('QueryGenerator', function() {
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}], arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}],
expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;') RETURNING *;" expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;') RETURNING *;"
}, { }, {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}], 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.000 +00:00') RETURNING *;" expectation: "INSERT INTO \"myTable\" (\"name\",\"birthday\") VALUES ('foo','2011-03-27 10:01:55.000 +00:00') RETURNING *;"
}, { }, {
arguments: ['myTable', {name: 'foo', foo: 1}], arguments: ['myTable', {name: 'foo', foo: 1}],
...@@ -378,7 +396,7 @@ describe('QueryGenerator', function() { ...@@ -378,7 +396,7 @@ describe('QueryGenerator', function() {
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;') RETURNING *;", expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;') RETURNING *;",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}], 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.000 +00:00') RETURNING *;", expectation: "INSERT INTO myTable (name,birthday) VALUES ('foo','2011-03-27 10:01:55.000 +00:00') RETURNING *;",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
...@@ -425,7 +443,7 @@ describe('QueryGenerator', function() { ...@@ -425,7 +443,7 @@ describe('QueryGenerator', function() {
arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]], arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]],
expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;" expectation: "INSERT INTO \"myTable\" (\"name\") VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;"
}, { }, {
arguments: ['myTable', [{name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55))}]], 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.000 +00:00'),('bar','2012-03-27 10:01:55.000 +00:00') RETURNING *;" expectation: "INSERT INTO \"myTable\" (\"name\",\"birthday\") VALUES ('foo','2011-03-27 10:01:55.000 +00:00'),('bar','2012-03-27 10:01:55.000 +00:00') RETURNING *;"
}, { }, {
arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]], arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]],
...@@ -466,7 +484,7 @@ describe('QueryGenerator', function() { ...@@ -466,7 +484,7 @@ describe('QueryGenerator', function() {
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;", expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
arguments: ['myTable', [{name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {name: 'bar', birthday: new Date(Date.UTC(2012, 2, 27, 10, 1, 55))}]], 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.000 +00:00'),('bar','2012-03-27 10:01:55.000 +00:00') RETURNING *;", expectation: "INSERT INTO myTable (name,birthday) VALUES ('foo','2011-03-27 10:01:55.000 +00:00'),('bar','2012-03-27 10:01:55.000 +00:00') RETURNING *;",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
...@@ -506,10 +524,10 @@ describe('QueryGenerator', function() { ...@@ -506,10 +524,10 @@ describe('QueryGenerator', function() {
updateQuery: [ updateQuery: [
{ {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], 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.000 +00:00' WHERE \"id\"=2 RETURNING *" expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.000 +00:00' WHERE \"id\"=2 RETURNING *"
}, { }, {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 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.000 +00:00' WHERE \"id\"=2 RETURNING *" expectation: "UPDATE \"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.000 +00:00' WHERE \"id\"=2 RETURNING *"
}, { }, {
arguments: ['myTable', {bar: 2}, {name: 'foo'}], arguments: ['myTable', {bar: 2}, {name: 'foo'}],
...@@ -533,7 +551,7 @@ describe('QueryGenerator', function() { ...@@ -533,7 +551,7 @@ describe('QueryGenerator', function() {
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *", expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *",
context: {options: {omitNull: true}} context: {options: {omitNull: true}}
}, { }, {
arguments: ['mySchema.myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], arguments: ['mySchema.myTable', {name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}, {id: 2}],
expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.000 +00:00' WHERE \"id\"=2 RETURNING *" expectation: "UPDATE \"mySchema\".\"myTable\" SET \"name\"='foo',\"birthday\"='2011-03-27 10:01:55.000 +00:00' WHERE \"id\"=2 RETURNING *"
}, { }, {
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}], arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}],
...@@ -542,11 +560,11 @@ describe('QueryGenerator', function() { ...@@ -542,11 +560,11 @@ describe('QueryGenerator', function() {
// Variants when quoteIdentifiers is false // Variants when quoteIdentifiers is false
{ {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], 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.000 +00:00' WHERE id=2 RETURNING *", expectation: "UPDATE myTable SET name='foo',birthday='2011-03-27 10:01:55.000 +00:00' WHERE id=2 RETURNING *",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
arguments: ['myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, 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.000 +00:00' WHERE id=2 RETURNING *", expectation: "UPDATE myTable SET name='foo',birthday='2011-03-27 10:01:55.000 +00:00' WHERE id=2 RETURNING *",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
...@@ -574,7 +592,7 @@ describe('QueryGenerator', function() { ...@@ -574,7 +592,7 @@ describe('QueryGenerator', function() {
expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *", expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *",
context: {options: {omitNull: true, quoteIdentifiers: false}}, context: {options: {omitNull: true, quoteIdentifiers: false}},
}, { }, {
arguments: ['mySchema.myTable', {name: 'foo', birthday: new Date(Date.UTC(2011, 2, 27, 10, 1, 55))}, {id: 2}], arguments: ['mySchema.myTable', {name: 'foo', birthday: moment("2011-03-27 10:01:55 +0000", "YYYY-MM-DD HH:mm:ss Z").toDate()}, {id: 2}],
expectation: "UPDATE mySchema.myTable SET name='foo',birthday='2011-03-27 10:01:55.000 +00:00' WHERE id=2 RETURNING *", expectation: "UPDATE mySchema.myTable SET name='foo',birthday='2011-03-27 10:01:55.000 +00:00' WHERE id=2 RETURNING *",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, { }, {
...@@ -758,19 +776,20 @@ describe('QueryGenerator', function() { ...@@ -758,19 +776,20 @@ describe('QueryGenerator', function() {
] ]
} }
Sequelize.Utils._.each(suites, function(tests, suiteTitle) { Helpers.Sequelize.Utils._.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() { describe(suiteTitle, function() {
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() { it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
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).toEqual(test.expectation)
done()
})
}) })
}) })
}) })
}) })
}) }
\ No newline at end of file
/* jshint multistr: true */
if (typeof require === 'function') {
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("comments", function() {
it("should create a comment for a column", function(done) {
var self = this
, User = this.sequelize.define('User', {
username: {type: this.DataTypes.STRING, comment: 'Some lovely info for my DBA'}
})
User.sync().success(function(){
var sql = ''
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\'';
}
else if (dialect === "postgres" || dialect === "postgres-native") {
sql = 'SELECT com.description as cmt FROM pg_attribute a JOIN pg_class pgc ON pgc.oid = a.attrelid \
LEFT JOIN pg_index i ON (pgc.oid = i.indrelid AND i.indkey[0] = a.attnum) \
LEFT JOIN pg_description com on (pgc.oid = com.objoid AND a.attnum = com.objsubid) \
WHERE a.attnum > 0 AND pgc.oid = a.attrelid AND pg_table_is_visible(pgc.oid) \
AND NOT a.attisdropped AND pgc.relname = \'Users\' AND a.attname = \'username\'';
}
else if (dialect === "sqlite") {
// sqlite doesn't support comments except for explicit comments in the file
expect(true).toBeTrue()
return done()
} else {
console.log('FIXME: This dialect is not supported :(');
expect(true).toBeTrue()
return done()
}
self.sequelize.query(sql, null, {raw: true}).success(function(result) {
expect(result[0].cmt).toEqual('Some lovely info for my DBA');
done()
})
})
})
})
})
if(typeof require === 'function') { if(typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, Helpers = require('./buster-helpers') , Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect() , dialect = Helpers.getTestDialect()
, moment = require('moment')
} }
var qq = function(str) { var qq = function(str) {
...@@ -15,6 +17,7 @@ var qq = function(str) { ...@@ -15,6 +17,7 @@ var qq = function(str) {
} }
buster.spec.expose() buster.spec.expose()
buster.timeout = 1000
describe(Helpers.getTestDialectTeaser("Sequelize"), function() { describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
before(function(done) { before(function(done) {
...@@ -25,6 +28,23 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() { ...@@ -25,6 +28,23 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
}) })
}) })
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() { describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() { it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).toBeFalse() expect(this.sequelize.isDefined('Project')).toBeFalse()
...@@ -47,7 +67,7 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() { ...@@ -47,7 +67,7 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
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.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) { this.User.sync().success(done).error(function(err) {
console(err) console.log(err)
done() done()
}) })
}) })
...@@ -166,6 +186,120 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() { ...@@ -166,6 +186,120 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
done() 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() { describe('define', function() {
...@@ -233,6 +367,5 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() { ...@@ -233,6 +367,5 @@ describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
}) })
}) })
}) })
}) })
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, dbFile = __dirname + '/test.sqlite'
, storages = [dbFile]
, DataTypes = require(__dirname + "/../../lib/data-types")
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect === 'sqlite') {
describe('[SQLITE] DAOFactory', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
age: DataTypes.INTEGER,
name: DataTypes.STRING,
bio: DataTypes.TEXT
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
})
})
storages.forEach(function(storage) {
describe('with storage "' + storage + '"', function() {
after(function(done) {
if (storage == dbFile) {
require("fs").unlink(__dirname + '/test.sqlite', done)
}
})
describe('create', function() {
it('creates a table entry', function(done) {
var self = this
this.User.create({ age: 21, name: 'John Wayne', bio: 'noot noot' }).success(function(user) {
expect(user.age).toEqual(21)
expect(user.name).toEqual('John Wayne')
expect(user.bio).toEqual('noot noot')
self.User.all().success(function(users) {
var usernames = users.map(function(user) {
return user.name
})
expect(usernames).toEqual(['John Wayne'])
done()
})
})
})
it('should allow the creation of an object with options as attribute', function(done) {
var Person = this.sequelize.define('Person', {
name: DataTypes.STRING,
options: DataTypes.TEXT
})
Person.sync({ force: true }).success(function() {
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
Person.create({
name: 'John Doe',
options: options
}).success(function(people) {
expect(people.options).toEqual(options)
done()
})
})
})
it('should allow the creation of an object with a boolean (true) as attribute', function(done) {
var Person = this.sequelize.define('Person', {
name: DataTypes.STRING,
has_swag: DataTypes.BOOLEAN
})
Person.sync({ force: true }).success(function() {
Person.create({
name: 'John Doe',
has_swag: true
}).success(function(people) {
expect(people.has_swag).toBeTruthy();
done()
})
})
})
it('should allow the creation of an object with a boolean (false) as attribute', function(done) {
var Person = this.sequelize.define('Person', {
name: DataTypes.STRING,
has_swag: DataTypes.BOOLEAN
})
Person.sync({ force: true }).success(function() {
Person.create({
name: 'John Doe',
has_swag: false
}).success(function(people) {
expect(people.has_swag).toBeFalsy();
done()
})
})
})
})
describe('.find', function() {
before(function(done) {
this.User.create({name: 'user', bio: 'footbar'}).success(done)
})
it("finds normal lookups", function(done) {
this.User.find({ where: { name:'user' } }).success(function(user) {
expect(user.name).toEqual('user')
done()
})
})
it("should make aliased attributes available", function(done) {
this.User.find({ where: { name:'user' }, attributes: ['id', ['name', 'username']] }).success(function(user) {
expect(user.username).toEqual('user')
done()
})
})
})
describe('.all', function() {
before(function(done) {
this.User.bulkCreate([
{name: 'user', bio: 'foobar'},
{name: 'user', bio: 'foobar'}
]).success(done)
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).toEqual(2)
done()
})
})
})
describe('.min', function() {
it("should return the min value", function(done) {
var self = this
, users = []
for (var i = 2; i < 5; i++) {
users[users.length] = {age: i}
}
this.User.bulkCreate(users).success(function() {
self.User.min('age').on('success', function(min) {
expect(min).toEqual(2)
done()
})
})
})
})
describe('.max', function() {
it("should return the max value", function(done) {
var self = this
, users = []
for (var i = 2; i <= 5; i++) {
users[users.length] = {age: i}
}
this.User.bulkCreate(users).success(function() {
self.User.max('age').on('success', function(min) {
expect(min).toEqual(5);
done()
})
})
})
})
})
})
})
}
...@@ -33,9 +33,7 @@ if (dialect === 'sqlite') { ...@@ -33,9 +33,7 @@ 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(oldUser) {
self.User self.User.create({ username: 'new user' }).success(function(newUser) {
.create({ username: 'new user' })
.success(function(newUser) {
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) {
...@@ -44,9 +42,6 @@ if (dialect === 'sqlite') { ...@@ -44,9 +42,6 @@ if (dialect === 'sqlite') {
}) })
}) })
}) })
.error(function(err) {
console.log(err)
})
}) })
}) })
}) })
......
var Sequelize = require("../../index") if(typeof require === 'function') {
, sequelize = new Sequelize(null, null, null, { dialect: 'sqlite' }) const buster = require("buster")
, Helpers = new (require("../config/helpers"))(sequelize) , Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/sqlite/query-generator") , QueryGenerator = require("../../lib/dialects/sqlite/query-generator")
, util = require("util"); , util = require("util");
}
describe('QueryGenerator', function() { buster.spec.expose()
beforeEach(function() { Helpers.sync() }) buster.testRunner.timeout = 1000
afterEach(function() { Helpers.drop() })
var suites = { if (dialect === 'sqlite') {
describe('[SQLITE] QueryGenerator', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
})
})
var suites = {
attributesToSQL: [ attributesToSQL: [
{ {
arguments: [{id: 'INTEGER'}], arguments: [{id: 'INTEGER'}],
...@@ -212,20 +232,23 @@ describe('QueryGenerator', function() { ...@@ -212,20 +232,23 @@ describe('QueryGenerator', function() {
expectation: "DELETE FROM `myTable` WHERE `name`='foo'" expectation: "DELETE FROM `myTable` WHERE `name`='foo'"
} }
] ]
}; }
Sequelize.Utils._.each(suites, function(tests, suiteTitle) { Helpers.Sequelize.Utils._.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)
it(title, function() { it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly // Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
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).toEqual(test.expectation)
done()
})
}) })
}) })
}) })
}) })
}); }
var Utils = require('../lib/utils') var buster = require("buster")
, Utils = require('../lib/utils')
, Helpers = require('./buster-helpers')
describe('Utils', function() { buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("Utils"), function() {
describe('removeCommentsFromFunctionString', function() { describe('removeCommentsFromFunctionString', function() {
it("removes line comments at the start of a line", function() { 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 result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString())
expect(result).toNotMatch(/.*noot.*/) expect(result).not.toMatch(/.*noot.*/)
done()
}) })
it("removes lines comments in the middle of a line", function() { it("removes lines comments in the middle of a line", function(done) {
var functionWithLineComments = function() { var functionWithLineComments = function() {
alert(1) // noot noot alert(1) // noot noot
} }
var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString()) var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString())
expect(result).toNotMatch(/.*noot.*/) expect(result).not.toMatch(/.*noot.*/)
done()
}) })
it("removes range comments", function() { it("removes range comments", function(done) {
var s = function() { var s = function() {
alert(1) /* alert(1) /*
noot noot noot noot
...@@ -31,97 +38,117 @@ describe('Utils', function() { ...@@ -31,97 +38,117 @@ describe('Utils', function() {
}.toString() }.toString()
var result = Utils.removeCommentsFromFunctionString(s) var result = Utils.removeCommentsFromFunctionString(s)
expect(result).toNotMatch(/.*noot.*/) expect(result).not.toMatch(/.*noot.*/)
expect(result).toNotMatch(/.*foo.*/) expect(result).not.toMatch(/.*foo.*/)
expect(result).toMatch(/.*alert\(2\).*/) expect(result).toMatch(/.*alert\(2\).*/)
done()
}) })
}) })
describe('argsArePrimaryKeys', function() { describe('argsArePrimaryKeys', function() {
it("doesn't detect primary keys if primareyKeys and values have different lengths", function() { 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])).toBeFalsy()
done()
}) })
it("doesn't detect primary keys if primary keys are hashes or arrays", function() { it("doesn't detect primary keys if primary keys are hashes or arrays", function(done) {
expect(Utils.argsArePrimaryKeys([[]], [1])).toBeFalsy() expect(Utils.argsArePrimaryKeys([[]], [1])).toBeFalsy()
done()
}) })
it('detects primary keys if length is correct and data types are matching', function() { 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"])).toBeTruthy()
done()
}) })
it("detects primary keys if primary keys are dates and lengths are matching", function() { 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'])).toBeTruthy()
done()
}) })
}) })
describe('underscore', function() { describe('underscore', function() {
describe('underscoredIf', function() { describe('underscoredIf', function() {
it('is defined', function() { it('is defined', function(done) {
expect(Utils._.underscoredIf).toBeDefined() expect(Utils._.underscoredIf).toBeDefined()
done()
}) })
it('underscores if second param is true', function() { it('underscores if second param is true', function(done) {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar') expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar')
done()
}) })
it("doesn't underscore if second param is false", function() { it("doesn't underscore if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', false)).toEqual('fooBar') expect(Utils._.underscoredIf('fooBar', false)).toEqual('fooBar')
done()
}) })
}) })
describe('camelizeIf', function() { describe('camelizeIf', function() {
it('is defined', function() { it('is defined', function(done) {
expect(Utils._.camelizeIf).toBeDefined() expect(Utils._.camelizeIf).toBeDefined()
done()
}) })
it('camelizes if second param is true', function() { it('camelizes if second param is true', function(done) {
expect(Utils._.camelizeIf('foo_bar', true)).toEqual('fooBar') expect(Utils._.camelizeIf('foo_bar', true)).toEqual('fooBar')
done()
}) })
it("doesn't camelize if second param is false", function() { it("doesn't camelize if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar') expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar')
done()
}) })
}) })
}) })
describe('isHash', function() { describe('isHash', function() {
it('doesn\'t match arrays', function() { it('doesn\'t match arrays', function(done) {
expect(Utils.isHash([])).toBeFalsy(); expect(Utils.isHash([])).toBeFalsy()
}); done()
it('doesn\'t match null', function() { })
expect(Utils.isHash(null)).toBeFalsy();
}); it('doesn\'t match null', function(done) {
it('matches plain objects', function() { expect(Utils.isHash(null)).toBeFalsy()
done()
})
it('matches plain objects', function(done) {
var values = { var values = {
'name': { 'name': {
'first': 'Foo', 'first': 'Foo',
'last': 'Bar' 'last': 'Bar'
} }
}; }
expect(Utils.isHash(values)).toBeTruthy(); expect(Utils.isHash(values)).toBeTruthy()
}); done()
it('matches plain objects with length property/key', function() { })
it('matches plain objects with length property/key', function(done) {
var values = { var values = {
'name': { 'name': {
'first': 'Foo', 'first': 'Foo',
'last': 'Bar' 'last': 'Bar'
}, },
'length': 1 'length': 1
}; }
expect(Utils.isHash(values)).toBeTruthy(); expect(Utils.isHash(values)).toBeTruthy()
}); done()
}); })
})
describe('format', function() { describe('format', function() {
it('should format where clause correctly when the value is truthy', function() { 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)).toEqual('foo = 1')
}); done()
})
it('should format where clause correctly when the value is falsy', function() {
var where = ['foo = ?', 0]; it('should format where clause correctly when the value is falsy', function(done) {
expect(Utils.format(where)).toEqual('foo = 0'); var where = ['foo = ?', 0]
}); expect(Utils.format(where)).toEqual('foo = 0')
}); done()
})
})
}) })
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!