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

Commit 83f94b36 by Daniel Durante

Merge branch 'master' into milestones/2.0.0

Conflicts:
	lib/emitters/custom-event-emitter.js
	spec-jasmine/dao.spec.js
	spec/associations/has-one.spec.js
	spec/dao.spec.js
	spec/promise.spec.js
	test/dao.validations.test.js
2 parents 15153227 610c80d0
Showing with 7192 additions and 8372 deletions
{
"globals": {
"jasmine": false,
"spyOn": false,
"it": false,
"console": false,
......
......@@ -3,8 +3,7 @@ before_script:
- "psql -c 'create database sequelize_test;' -U postgres"
script:
- "npm run test-buster-travis"
- "npm run test-jasmine"
- "make test"
notifications:
email:
......
REPORTER ?= dot
TESTS = $(shell find ./test/* -name "*.test.js")
DIALECT ?= mysql
# test commands
teaser:
@echo "" && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo '# Running tests for $(DIALECT) #' && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo ''
test:
@make teaser && \
./node_modules/mocha/bin/mocha \
--colors \
--reporter $(REPORTER) \
$(TESTS)
sqlite:
@DIALECT=sqlite make test
mysql:
@DIALECT=mysql make test
postgres:
@DIALECT=postgres make test
postgres-native:
@DIALECT=postgres-native make test
# test aliases
pgsql: postgres
postgresn: postgres-native
# test all the dialects \o/
all: sqlite mysql postgres postgres-native
.PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test
\ No newline at end of file
# 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.
<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>
MySQL, PostgresSQL, and SQLite Object Relational Mapper (ORM) for [node](http://nodejs.org).
## Important Notes ##
......@@ -35,6 +32,7 @@ changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/
- Asynchronous library
- Associations
- Importing definitions from single files
- Promises
## Documentation and Updates ##
......@@ -64,7 +62,7 @@ A very basic roadmap. Chances aren't too bad, that not mentioned things are impl
- 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
- 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
- Move validation of enum attribute value to validate method
- BLOB [#99](https://github.com/sequelize/sequelize/issues/99)
......@@ -142,9 +140,7 @@ $ npm install
### 4. Run the tests ###
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
(which contains the ugly and awkward node-jasmine based tests). A main goal
is to get rid of the jasmine tests!
lovely [BusterJS](http://busterjs.org) tests).
As you might haven't installed all of the supported SQL dialects, here is how
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:
$ npm test
$ # run only the jasmine tests (for all dialects):
$ npm run test-jasmine
$ # run all of the buster specs (for all dialects):
$ npm run test-buster
......@@ -167,6 +160,9 @@ $ npm run test-buster-sqlite
$ # run the buster specs for postgresql:
$ 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 ###
......@@ -235,6 +231,17 @@ for (var key in obj) {
```js
{
"globals": {
"spyOn": false,
"it": false,
"console": false,
"describe": false,
"expect": false,
"beforeEach": false,
"waits": false,
"waitsFor": false,
"runs": false
},
"camelcase": true,
"curly": true,
"forin": true,
......@@ -246,10 +253,3 @@ for (var key in obj) {
"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)
......@@ -20,6 +20,10 @@
- [BUG] PostgreSQL should now be able to insert empty arrays with typecasting. [#718](https://github.com/sequelize/sequelize/pull/718). 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] 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 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] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
......@@ -33,11 +37,16 @@
- [FEATURE] `findOrCreate` now returns an additional flag (`created`), that is true if a model was created, and false if it was found [#648](https://github.com/sequelize/sequelize/pull/648). janmeier
- [FEATURE] Field and table comments for MySQL and PG. [#523](https://github.com/sequelize/sequelize/pull/523). MySQL by iamjochen. PG by janmeier
- [FEATURE] BigInts can now be used for autoincrement/serial columns. [#673](https://github.com/sequelize/sequelize/pull/673). thanks to sevastos
- [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
- [FEATURE] Use moment for better postgres timestamp strings. [#710](https://github.com/sequelize/sequelize/pull/710). Thanks to seth-admittedly
- [FEATURE] Keep milliseconds in timestamps for postgres. [#712](https://github.com/sequelize/sequelize/pull/712). Thanks to seth-admittedly
- [FEATURE] You can now set lingo's language through Sequelize. [#713](https://github.com/sequelize/sequelize/pull/713). Thanks to durango
- [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] 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] 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
# v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
/*jslint node:true */
"use strict";
var Sequelize = require('sequelize');
// initialize database connection
var sequelize = new Sequelize('testsequelize', 'testsequelize', 'testsequelize', {
dialect: 'postgres',
port: 5432,
define: {
freezeTableName: true
}
});
// load models
var models = [
'Trainer',
'Series',
'Video'
];
models.forEach(function(model) {
module.exports[model] = sequelize.import(__dirname + '/' + model);
});
// describe relationships
(function(m) {
m.Series.hasOne(m.Video);
m.Trainer.hasMany(m.Series);
})(module.exports);
// export connection
module.exports.sequelize = sequelize;
\ No newline at end of file
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Series', {
title: {
type: DataTypes.STRING
},
sub_title: {
type: DataTypes.STRING
},
description: {
type: DataTypes.TEXT
},
// Set FK relationship (hasMany) with `Trainer`
trainer_id: {
type: DataTypes.INTEGER,
references: "Trainer",
referencesKey: 'id'
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Trainer', {
first_name: {
type: DataTypes.STRING
},
last_name: {
type: DataTypes.STRING
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
/*jslint node:true */
"use strict";
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Video', {
title: {
type: DataTypes.STRING
},
sequence: {
type: DataTypes.INTEGER
},
description: {
type: DataTypes.TEXT
},
// set relationship (hasOne) with `Series`
series_id: {
type: DataTypes.INTEGER,
references: "Series",
referencesKey: 'id'
}
}, {
// don't need timestamp attributes for this model
timestamps: false,
underscored: true
});
};
Results after syncing the dabatase
----------------------------------
After syncing the database, you'll see in the console the following:
```
Executing: CREATE TABLE IF NOT EXISTS "Trainer" ("first_name" VARCHAR(255), "last_name" VARCHAR(255), "id" SERIAL , PRIMARY KEY ("id"));
Executing: CREATE TABLE IF NOT EXISTS "Series" ("title" VARCHAR(255), "sub_title" VARCHAR(255), "description" TEXT, "trainer_id" INTEGER REFERENCES "Trainer" ("id"), "id" SERIAL , PRIMARY KEY ("id"));
Executing: CREATE TABLE IF NOT EXISTS "Video" ("title" VARCHAR(255), "sequence" INTEGER, "description" TEXT, "series_id" INTEGER REFERENCES "Series" ("id"), "id" SERIAL , PRIMARY KEY ("id"));
```
Notice in the `Video` that `series_id` field has a referential integrity to `Series`:
```
"series_id" INTEGER REFERENCES "Series" ("id")
```
This is the output when describing the table's structure of the Postgres database:
**Trainer** table:
```
testsequelize=> \d+ "Trainer";
Table "public.Trainer"
Column | Type | Modifiers |
------------+------------------------+--------------------------------------------------------+
first_name | character varying(255) | |
last_name | character varying(255) | |
id | integer | not null default nextval('"Trainer_id_seq"'::regclass) |
Indexes:
"Trainer_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE ""Series"" CONSTRAINT "Series_trainer_id_fkey" FOREIGN KEY (trainer_id) REFERENCES "Trainer"(id)
Has OIDs: no
```
**Series** table:
```
testsequelize=> \d+ "Series";
Table "public.Series"
Column | Type | Modifiers |
-----------------+------------------------+-------------------------------------------------------+
title | character varying(255) | |
sub_title | character varying(255) | |
description | text | |
trainer_id | integer | |
id | integer | not null default nextval('"Series_id_seq"'::regclass) |
Indexes:
"Series_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"Series_trainer_id_fkey" FOREIGN KEY (trainer_id) REFERENCES "Trainer"(id)
Referenced by:
TABLE ""Video"" CONSTRAINT "Video_series_id_fkey" FOREIGN KEY (series_id) REFERENCES "Series"(id)
Has OIDs: no
```
**Video** table:
```
testsequelize=> \d+ "Video";
Table "public.Video"
Column | Type | Modifiers |
-------------+------------------------+------------------------------------------------------+
title | character varying(255) | |
sequence | integer | |
description | text | |
series_id | integer | |
id | integer | not null default nextval('"Video_id_seq"'::regclass) |
Indexes:
"Video_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"Video_series_id_fkey" FOREIGN KEY (series_id) REFERENCES "Series"(id)
Has OIDs: no
```
......@@ -21,12 +21,26 @@ module.exports = (function() {
where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"}
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) {
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;
});
Utils._.extend(options.where, where)
options.where = Utils._.extend(options.where, where)
}
} else {
options.where = where;
}
......
......@@ -12,8 +12,17 @@ module.exports = (function() {
where[this.__factory.identifier] = this.instance.id
options.where = options.where ? Utils._.extend(options.where, where) : where
return this.__factory.target.findAll(options)
if (options.where) {
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) {
......@@ -34,7 +43,7 @@ module.exports = (function() {
if (obsoleteAssociations.length > 0) {
// clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = null
associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id
})
......@@ -46,12 +55,12 @@ module.exports = (function() {
if (unassociatedObjects.length > 0) {
// set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = self.instance.id
associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id
})
update = {}
update[self.__factory.identifier] = self.instance.id
update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
chainer.add(this.__factory.target.update(update, { id: unassociatedIds }))
}
......
......@@ -56,6 +56,12 @@ module.exports = (function() {
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)
}
......@@ -64,17 +70,25 @@ module.exports = (function() {
HasOne.prototype.injectSetter = function(obj) {
var self = this
, options = self.options || {}
obj[this.accessors.set] = function(associatedObject) {
var instance = this;
var instance = this
return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]().success(function(oldObj) {
if (oldObj) {
oldObj[self.identifier] = null
oldObj.save()
oldObj.save().success(function() {
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
} else {
emitter.emit('success', null)
}
})
} else {
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject
......@@ -84,7 +98,7 @@ module.exports = (function() {
} else {
emitter.emit('success', null)
}
}
})
}).run()
}
......
var Toposort = require('toposort-class')
, DaoFactory = require('./dao-factory')
module.exports = (function() {
var DAOFactoryManager = function(sequelize) {
......@@ -43,12 +44,13 @@ module.exports = (function() {
, sorter = new Toposort()
this.daos.forEach(function(dao) {
daos[dao.tableName] = dao
var deps = []
for(var attrName in dao.rawAttributes) {
if(dao.rawAttributes.hasOwnProperty(attrName)) {
if(dao.rawAttributes[attrName].references) {
daos[dao.tableName] = dao
for (var attrName in dao.rawAttributes) {
if (dao.rawAttributes.hasOwnProperty(attrName)) {
if (dao.rawAttributes[attrName].references) {
deps.push(dao.rawAttributes[attrName].references)
}
}
......
......@@ -5,8 +5,6 @@ var Utils = require("./utils")
module.exports = (function() {
var DAOFactory = function(name, attributes, options) {
var self = this
this.options = Utils._.extend({
timestamps: true,
instanceMethods: {},
......@@ -24,18 +22,25 @@ module.exports = (function() {
// error check options
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)
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)
}
})
this.name = name
if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? name : Utils.pluralize(name, this.options.language)
} else {
this.tableName = this.options.tableName
}
attributes = replaceReferencesWithTableNames(attributes)
this.rawAttributes = attributes
this.daoFactoryManager = null // defined in init function
this.associations = {}
......@@ -59,8 +64,8 @@ module.exports = (function() {
var self = this;
this.daoFactoryManager = daoFactoryManager
this.primaryKeys = {};
Utils._.each(this.attributes, function(dataTypeString, attributeName) {
if ((attributeName !== 'id') && (dataTypeString.indexOf('PRIMARY KEY') !== -1)) {
self.primaryKeys[attributeName] = dataTypeString
......@@ -75,9 +80,11 @@ module.exports = (function() {
findAutoIncrementField.call(this)
// DAO prototype
// WTF ... ?
this.DAO = function() {
DAO.apply(this, arguments);
};
}
Util.inherits(this.DAO, DAO);
this.DAO.prototype.rawAttributes = this.rawAttributes;
......@@ -89,47 +96,48 @@ module.exports = (function() {
}
Utils._.each(['Get', 'Set'], function(type) {
var prop = type.toLowerCase(),
opt = prop + 'terMethods',
meth = '__define' + type + 'ter__',
funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {}
;
var prop = type.toLowerCase()
, opt = prop + 'terMethods'
, meth = '__define' + type + 'ter__'
, funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {}
Utils._.each(self.rawAttributes, function(attr, name) {
if (attr.hasOwnProperty(prop))
if (attr.hasOwnProperty(prop)) {
funcs[name] = attr[prop]
});
}
})
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.')
}
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.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) {
if (((definition === DataTypes.BOOLEAN) || (definition.type === DataTypes.BOOLEAN))) {
self.DAO.prototype.booleanValues.push(name);
}
if (definition.hasOwnProperty('defaultValue')) {
self.DAO.prototype.defaultValues[name] = function() {
return Utils.toDefaultValue(definition.defaultValue);
return Utils.toDefaultValue(definition.defaultValue)
}
}
if (definition.hasOwnProperty('validate')) {
self.DAO.prototype.validators[name] = definition.validate;
}
});
})
this.DAO.prototype.__factory = this;
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues);
this.DAO.prototype.__factory = this
this.DAO.prototype.hasDefaultValues = !Utils._.isEmpty(this.DAO.prototype.defaultValues)
return this
}
......@@ -278,6 +286,17 @@ module.exports = (function() {
// whereCollection is used for non-primary key updates
this.options.whereCollection = options.where || null
} else if (typeof options === "string") {
var where = {}
, keys = Object.keys(primaryKeys)
if (this.primaryKeyCount === 1) {
where[primaryKeys[keys[0]]] = options;
options = where;
} else if (this.primaryKeyCount < 1) {
// Revert to default behavior which is {where: [int]}
options = {where: parseInt(Number(options) || 0, 0)}
}
}
options.limit = 1
......@@ -421,39 +440,26 @@ module.exports = (function() {
, updatedAtAttr = self.options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = self.options.underscored ? 'created_at' : 'createdAt'
fields = fields || []
// we will re-create from DAOs, which may have set up default attributes
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) {
var values = {};
var values = fields.length > 0 ? {} : dao.dataValues
fields.forEach(function(field) {
values[field] = dao.dataValues[field]
})
if (self.options.timestamps) {
values[createdAtAttr] = Utils.now()
values[updatedAtAttr] = Utils.now()
}
records.push(values);
})
} else {
daos.forEach(function(dao) {
records.push(dao.dataValues)
records.push(values);
})
}
// Validate enums
records.forEach(function(values) {
......@@ -509,6 +515,10 @@ module.exports = (function() {
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where)
}
DAOFactory.prototype.describe = function() {
return this.QueryInterface.describeTable(this.tableName)
}
// private
var query = function() {
......@@ -616,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"))
return DAOFactory
......
......@@ -399,6 +399,20 @@ module.exports = (function() {
for (key in attrs) {
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
......
......@@ -4,11 +4,18 @@ var mysql
, Utils = require("../../utils")
, 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() {
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.client = null
this.config = config || {}
......@@ -75,7 +82,9 @@ module.exports = (function() {
read: Pooling.Pool({
name: 'sequelize-read',
create: function (done) {
if (reads >= self.config.replication.read.length) reads = 0;
if (reads >= self.config.replication.read.length) {
reads = 0
}
var config = self.config.replication.read[reads++];
connect.call(self, function (err, connection) {
......@@ -161,11 +170,12 @@ module.exports = (function() {
query.done(function() {
self.pendingQueries--;
if (self.pool) self.pool.release(query.client);
else {
if (self.pool) {
self.pool.release(query.client);
} else {
if (self.pendingQueries === 0) {
setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self);
self.pendingQueries === 0 && self.disconnect.call(self)
}, 100);
}
}
......@@ -173,16 +183,16 @@ module.exports = (function() {
if (!this.pool) {
query.run(sql);
}
else {
} else {
this.pool.acquire(function(err, client) {
if (err) return query.emit('error', err);
if (err) {
return query.emit('error', err)
}
query.client = client;
query.run(sql);
query.client = client
query.run(sql)
return;
}, undefined, options.type);
}, undefined, options.type)
}
return query;
......@@ -202,8 +212,10 @@ module.exports = (function() {
};
ConnectorManager.prototype.disconnect = function() {
if (this.client) disconnect.call(this, this.client);
return;
if (this.client) {
disconnect.call(this, this.client)
}
return
};
......@@ -258,12 +270,12 @@ module.exports = (function() {
switch(err.code) {
case 'ECONNREFUSED':
case 'ER_ACCESS_DENIED_ERROR':
emitter.emit('error', new Error("Failed to authenticate for MySQL. Please double check your settings."))
emitter.emit('error', 'Failed to authenticate for MySQL. Please double check your settings.')
break
case 'ENOTFOUND':
case 'EHOSTUNREACH':
case 'EINVAL':
emitter.emit('error', new Error("Failed to find MySQL server. Please double check your settings."))
emitter.emit('error', 'Failed to find MySQL server. Please double check your settings.')
break
}
}
......@@ -288,7 +300,7 @@ module.exports = (function() {
}
var validateConnection = function(client) {
return client && client.state != 'disconnected'
return client && client.state !== 'disconnected'
}
var enqueue = function(queueItem, options) {
......@@ -335,8 +347,6 @@ module.exports = (function() {
}
var afterQuery = function(queueItem) {
var self = this
dequeue.call(this, queueItem)
transferQueuedItems.call(this, this.maxConcurrentQueries - this.activeQueue.length)
disconnectIfNoConnections.call(this)
......
var Utils = require("../../utils")
, DataTypes = require("../../data-types")
, SqlString = require("../../sql-string")
, util = require("util")
module.exports = (function() {
......@@ -28,9 +29,8 @@ module.exports = (function() {
return Utils._.template(query)({})
},
dropSchema: function() {
var query = "SHOW TABLES"
return Utils._.template(query)({})
dropSchema: function(tableName, options) {
return QueryGenerator.dropTableQuery(tableName, options)
},
showSchemasQuery: function() {
......@@ -217,8 +217,14 @@ module.exports = (function() {
query += " ORDER BY " + options.order
}
if (options.limit && !(options.include && (options.limit === 1))) {
if (options.offset && !options.limit) {
/*
* If no limit is defined, our best bet is to use the max number of rows in a table. From the MySQL docs:
* There is a limit of 2^32 (~4.295E+09) rows in a MyISAM table. If you build MySQL with the --with-big-tables option,
* the row limitation is increased to (2^32)^2 (1.844E+19) rows.
*/
query += " LIMIT " + options.offset + ", " + 18440000000000000000;
} else if (options.limit && !(options.include && (options.limit === 1))) {
if (options.offset) {
query += " LIMIT " + options.offset + ", " + options.limit
} else {
......@@ -529,7 +535,6 @@ module.exports = (function() {
if(dataType.references) {
template += " REFERENCES " + this.quoteIdentifier(dataType.references)
if(dataType.referencesKey) {
template += " (" + this.quoteIdentifier(dataType.referencesKey) + ")"
} else {
......@@ -547,7 +552,7 @@ module.exports = (function() {
}
if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
template += " COMMENT " + Utils.escape(dataType.comment)
template += " COMMENT " + this.escape(dataType.comment)
}
result[name] = template
......@@ -594,12 +599,7 @@ module.exports = (function() {
},
escape: function(value) {
if (value instanceof Date) {
value = Utils.toSqlDate(value)
} else if (typeof value === 'boolean') {
value = value ? 1 : 0
}
return Utils.escape(value)
return SqlString.escape(value, false, null, "mysql")
}
}
......
......@@ -3,61 +3,76 @@ var Query = require("./query")
module.exports = (function() {
var ConnectorManager = function(sequelize, config) {
var pgModule = config.dialectModulePath || 'pg'
this.sequelize = sequelize
this.client = null
this.config = config || {}
this.config.port = this.config.port || 5432
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg')
this.pooling = (!!this.config.pool && (this.config.pool.maxConnections > 0))
this.pg = this.config.native ? require(pgModule).native : require(pgModule)
this.poolIdentifier = null
// Better support for BigInts
// https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935
this.pg.types.setTypeParser(20, String);
// set pooling parameters if specified
if (this.pooling) {
this.pg.defaults.poolSize = this.config.poolCfg.maxConnections
this.pg.defaults.poolIdleTimeout = this.config.poolCfg.maxIdleTime
this.pg.defaults.poolSize = this.config.pool.maxConnections
this.pg.defaults.poolIdleTimeout = this.config.pool.maxIdleTime
}
this.disconnectTimeoutId = null
this.pendingQueries = 0
this.maxConcurrentQueries = (this.config.maxConcurrentQueries || 50)
process.on('exit', function() {
this.disconnect()
}.bind(this))
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
var isConnecting = false
var isConnected = false
ConnectorManager.prototype.query = function(sql, callee, options) {
ConnectorManager.prototype.endQuery = function() {
var self = this
if (this.client === null) {
this.connect()
if (!self.pooling && self.pendingQueries === 0) {
setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self)
}, 100)
}
var query = new Query(this.client, this.sequelize, callee, options || {})
self.pendingQueries += 1
return query.run(sql)
.success(function() { self.endQuery.call(self) })
.error(function() { self.endQuery.call(self) })
}
ConnectorManager.prototype.endQuery = function() {
ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this
self.pendingQueries -= 1
if (self.pendingQueries == 0) {
setTimeout(function() {
self.pendingQueries == 0 && self.disconnect.call(self)
}, 100)
}
self.pendingQueries++
return new Utils.CustomEventEmitter(function(emitter) {
self.connect()
.on('error', function(err) {
emitter.emit('error', err)
})
.on('success', function(done) {
var query = new Query(self.client, self.sequelize, callee, options || {})
done = done || null
query.run(sql, done)
.success(function(results) { emitter.emit('success', results); self.endQuery.call(self) })
.error(function(err) { emitter.emit('error', err); self.endQuery.call(self) })
.on('sql', function(sql) { emitter.emit('sql', sql) })
})
}).run().complete(function() { self.pendingQueries-- })
}
ConnectorManager.prototype.connect = function() {
ConnectorManager.prototype.connect = function(callback) {
var self = this
var emitter = new (require('events').EventEmitter)()
// in case database is slow to connect, prevent orphaning the client
if (this.isConnecting) {
return
emitter.emit('success')
return emitter
}
this.isConnecting = true
......@@ -65,10 +80,14 @@ module.exports = (function() {
var uri = this.sequelize.getQueryInterface().QueryGenerator.databaseConnectionUri(this.config)
var connectCallback = function(err, client) {
var connectCallback = function(err, client, done) {
self.isConnecting = false
if (!!err) {
// release the pool immediately, very important.
done && done(err)
if (err.code) {
switch(err.code) {
case 'ECONNREFUSED':
emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
......@@ -80,21 +99,25 @@ module.exports = (function() {
break
default:
emitter.emit('error', err)
break
}
}
} else if (client) {
client.query("SET TIME ZONE 'UTC'")
.on('end', function() {
client.query("SET TIME ZONE 'UTC'").on('end', function() {
self.isConnected = true
this.client = client
self.client = client
emitter.emit('success', done)
});
} else {
this.client = null
self.client = null
emitter.emit('success', done)
}
}
if (this.pooling) {
// acquire client from pool
this.pg.connect(uri, connectCallback)
this.poolIdentifier = this.pg.pools.getOrCreate(this.sequelize.config)
this.poolIdentifier.connect(connectCallback)
} else {
//create one-off client
this.client = new this.pg.Client(uri)
......@@ -105,8 +128,14 @@ module.exports = (function() {
}
ConnectorManager.prototype.disconnect = function() {
var self = this
if (this.client) this.client.end()
if (this.poolIdentifier) {
this.poolIdentifier.destroyAllNow()
}
if (this.client) {
this.client.end()
}
this.client = null
this.isConnecting = false
this.isConnected = false
......
var Utils = require("../../utils")
, util = require("util")
, DataTypes = require("../../data-types")
, SqlString = require("../../sql-string")
, tables = {}
, primaryKeys = {}
, moment = require("moment")
module.exports = (function() {
var QueryGenerator = {
......@@ -112,7 +112,7 @@ module.exports = (function() {
},
describeTableQuery: function(tableName) {
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS special FROM information_schema.columns c WHERE table_name = <%= table %>;'
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %>;'
return Utils._.template(query)({
table: this.escape(tableName)
})
......@@ -293,7 +293,6 @@ module.exports = (function() {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
// Remove serials that are null or undefined, which causes an error in PG
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName]) {
......@@ -725,10 +724,6 @@ module.exports = (function() {
return (i < 10) ? '0' + i.toString() : i.toString()
},
pgSqlDate: function (dt) {
return moment(dt).format("YYYY-MM-DD HH:mm:ss.SSS Z")
},
pgDataTypeMapping: function (tableName, attr, dataType) {
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys[tableName].push(attr)
......@@ -779,33 +774,8 @@ module.exports = (function() {
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.')
},
escape: function (val, field) {
if (val === undefined || val === null) {
return 'NULL';
}
switch (typeof val) {
case 'boolean':
return (val) ? 'true' : 'false';
case 'number':
return val + '';
case 'object':
if (Array.isArray(val)) {
var ret = 'ARRAY['+ val.map(function(it) { return this.escape(it) }.bind(this)).join(',') + ']'
if (!!field && !!field.type) {
ret += '::' + field.type.replace(/\(\d+\)/g, '')
}
return ret
}
}
if (val instanceof Date) {
val = this.pgSqlDate(val);
}
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
val = val.replace(/'/g, "''");
return "'" + val + "'";
escape: function(value, field) {
return SqlString.escape(value, false, null, "postgres", field)
}
}
......
......@@ -18,8 +18,9 @@ module.exports = (function() {
}
Utils.inherit(Query, AbstractQuery)
Query.prototype.run = function(sql) {
Query.prototype.run = function(sql, done) {
this.sql = sql
var self = this
if (this.options.logging !== false) {
this.options.logging('Executing: ' + this.sql)
......@@ -39,13 +40,14 @@ module.exports = (function() {
}.bind(this))
query.on('end', function() {
done && done()
this.emit('sql', this.sql)
if (receivedError) {
return
}
onSuccess.call(this, rows)
onSuccess.call(this, rows, sql)
}.bind(this))
return this
......@@ -55,11 +57,11 @@ module.exports = (function() {
return 'id'
}
var onSuccess = function(rows) {
var onSuccess = function(rows, sql) {
var results = []
, self = this
, isTableNameQuery = (this.sql.indexOf('SELECT table_name FROM information_schema.tables') === 0)
, isRelNameQuery = (this.sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0)
, isTableNameQuery = (sql.indexOf('SELECT table_name FROM information_schema.tables') === 0)
, isRelNameQuery = (sql.indexOf('SELECT relname FROM pg_class WHERE oid IN') === 0)
if (isTableNameQuery || isRelNameQuery) {
if (isRelNameQuery) {
......@@ -111,7 +113,7 @@ module.exports = (function() {
} else {
// Postgres will treat tables as case-insensitive, so fix the case
// of the returned values to match attributes
if(this.sequelize.options.quoteIdentifiers == false) {
if(this.options.raw === false && this.sequelize.options.quoteIdentifiers === false) {
var attrsMap = Utils._.reduce(this.callee.attributes, function(m, v, k) { m[k.toLowerCase()] = k; return m}, {})
rows.forEach(function(row) {
Utils._.keys(row).forEach(function(key) {
......
......@@ -289,7 +289,7 @@ module.exports = (function() {
Escape a value (e.g. a string, number or date)
*/
escape: function(value) {
throwMethodUndefined('quoteIdentifier')
throwMethodUndefined('escape')
}
}
......
var Utils = require("../../utils")
, sqlite3 = require('sqlite3').verbose()
var sqlite3
, Utils = require("../../utils")
, Query = require("./query")
module.exports = (function() {
var ConnectorManager = function(sequelize) {
var ConnectorManager = function(sequelize, config) {
this.sequelize = sequelize
if (config.dialectModulePath) {
sqlite3 = require(config.dialectModulePath).verbose()
} else {
sqlite3 = require('sqlite3').verbose()
}
}
Utils._.extend(ConnectorManager.prototype, require("../connector-manager").prototype)
......@@ -16,7 +22,7 @@ module.exports = (function() {
this.database = db = new sqlite3.Database(self.sequelize.options.storage || ':memory:', function(err) {
if (err) {
if (err.code === "SQLITE_CANTOPEN") {
emitter.emit('error', new Error("Failed to find SQL server. Please double check your settings."))
emitter.emit('error', 'Failed to find SQL server. Please double check your settings.')
}
}
......
var Utils = require("../../utils")
, DataTypes = require("../../data-types")
, SqlString = require("../../sql-string")
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
......@@ -36,9 +37,8 @@ module.exports = (function() {
return Utils._.template(query)({})
},
dropSchema: function() {
var query = "SELECT name FROM sqlite_master WHERE type='table' and name!='sqlite_sequence';"
return Utils._.template(query)({})
dropSchema: function(tableName, options) {
return this.dropTableQuery(tableName, options)
},
showSchemasQuery: function() {
......@@ -88,6 +88,16 @@ module.exports = (function() {
return this.replaceBooleanDefaults(sql)
},
dropTableQuery: function(tableName, options) {
options = options || {}
var query = "DROP TABLE IF EXISTS <%= table %>;"
return Utils._.template(query)({
table: this.quoteIdentifier(tableName)
})
},
addColumnQuery: function() {
var sql = MySqlQueryGenerator.addColumnQuery.apply(this, arguments)
return this.replaceBooleanDefaults(sql)
......@@ -133,6 +143,7 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
selectQuery: function(tableName, options) {
var table = null,
joinQuery = ""
......@@ -188,8 +199,13 @@ module.exports = (function() {
query += " ORDER BY " + options.order
}
if (options.limit && !(options.include && (options.limit === 1))) {
if (options.offset && !options.limit) {
/*
* If no limit is defined, our best bet is to use the max number of rows in a table. From the SQLite docs:
* A 140 terabytes database can hold no more than approximately 1e+13 rows
*/
query += " LIMIT " + options.offset + ", " + 10000000000000;
} else if (options.limit && !(options.include && (options.limit === 1))) {
if (options.offset) {
query += " LIMIT " + options.offset + ", " + options.limit
} else {
......@@ -372,7 +388,7 @@ module.exports = (function() {
},
removeIndexQuery: function(tableName, indexNameOrAttributes) {
var sql = "DROP INDEX <%= indexName %>"
var sql = "DROP INDEX IF EXISTS <%= indexName %>"
, indexName = indexNameOrAttributes
if (typeof indexName !== 'string') {
......@@ -448,19 +464,7 @@ module.exports = (function() {
},
escape: function(value) {
if (value instanceof Date) {
value = Utils.toSqlDate(value)
}
if (typeof value === 'string') {
return "'" + value.replace(/'/g, "''") + "'";
} else if (typeof value === 'boolean') {
return value ? 1 : 0; // SQLite has no type boolean
} else if (value === null || value === undefined) {
return 'NULL';
} else {
return value;
}
return SqlString.escape(value, false, null, "sqlite")
}
}
......
......@@ -54,6 +54,12 @@ module.exports = (function() {
return this
}
CustomEventEmitter.prototype.sql =
function(fct) {
this.on('sql', bindToProcess(fct))
return this;
}
// emit the events on the foreign emitter once events got triggered for me
CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) {
......
var moment = require("moment")
, path = require("path")
, Utils = require("./utils")
, DataTypes = require("./data-types")
, QueryInterface = require("./query-interface")
module.exports = (function() {
var Migration = function(migrator, path) {
var Migration = function(migrator, p) {
this.migrator = migrator
this.path = path
this.filename = Utils._.last(this.path.split('/'))
this.path = path.normalize(p)
this.filename = Utils._.last(this.path.split(path.sep))
var parsed = Migration.parseFilename(this.filename)
......
const fs = require("fs")
, moment = require("moment")
var Utils = require("./utils")
, Migration = require("./migration")
, DataTypes = require("./data-types")
var Utils = require(__dirname + "/utils")
, Migration = require(__dirname + "/migration")
, DataTypes = require(__dirname + "/data-types")
module.exports = (function() {
var Migrator = function(sequelize, options) {
......@@ -45,7 +45,7 @@ module.exports = (function() {
if (err) {
emitter.emit('error', err)
} else {
var chainer = new Utils.QueryChainer
var chainer = new Utils.QueryChainer()
, from = migrations[0]
if (options.method === 'down') {
......@@ -177,6 +177,56 @@ module.exports = (function() {
}).run()
}
/**
* Explicitly executes one or multiple migrations.
*
* @param filename {String|Array} Absolute filename(s) of the migrations script
* @param options {Object} Can contain three functions, before, after and success, which are executed before
* or after each migration respectively, with one parameter, the migration.
*/
Migrator.prototype.exec = function(filename, options) {
var self = this;
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
var addMigration = function(filename) {
self.options.logging('Adding migration script at ' + filename)
var migration = new Migration(self, filename)
chainer.add(migration, 'execute', [{ method: 'up' }], {
before: function(migration) {
if (options && Utils._.isFunction(options.before)) {
options.before.call(self, migration);
}
},
after: function(migration) {
if (options && Utils._.isFunction(options.after)) {
options.after.call(self, migration);
}
},
success: function(migration, callback) {
if (options && Utils._.isFunction(options.success)) {
options.success.call(self, migration);
}
callback();
}
})
}
if (Utils._.isArray(filename)) {
Utils._.each(filename, function(f) {
addMigration(f);
})
} else {
addMigration(filename);
}
chainer
.runSerially({ skipOnError: true })
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('error', err) })
}).run()
}
// private
var getLastMigrationFromDatabase = function() {
......
var Utils = require("./utils")
var Utils = require(__dirname + "/utils")
module.exports = (function() {
var QueryChainer = function(emitters) {
......
var Utils = require('./utils')
, DataTypes = require('./data-types')
, SQLiteQueryInterface = require('./dialects/sqlite/query-interface')
var Utils = require(__dirname + '/utils')
, DataTypes = require(__dirname + '/data-types')
, SQLiteQueryInterface = require(__dirname + '/dialects/sqlite/query-interface')
module.exports = (function() {
var QueryInterface = function(sequelize) {
......@@ -54,7 +54,7 @@ module.exports = (function() {
var showSchemasSql = self.QueryGenerator.showSchemasQuery()
self.sequelize.query(showSchemasSql, null, { raw: true }).success(function(schemaNames) {
self.emit('showAllSchemas', null)
emitter.emit('success', Utils._.flatten(Utils._.map(schemaNames, function(value){ return value.schema_name })))
emitter.emit('success', Utils._.flatten(Utils._.map(schemaNames, function(value){ return (!!value.schema_name ? value.schema_name : value) })))
}).error(function(err) {
self.emit('showAllSchemas', err)
emitter.emit('error', err)
......@@ -266,23 +266,128 @@ module.exports = (function() {
}
QueryInterface.prototype.update = function(dao, tableName, values, identifier) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier)
return queryAndEmit.call(this, [sql, dao], 'update')
var self = this
, restrict = false
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
// Check for a restrict field
if (!!dao.daoFactory && !!dao.daoFactory.associations) {
var keys = Object.keys(dao.daoFactory.associations)
, length = keys.length
for (var i = 0; i < length; i++) {
if (dao.daoFactory.associations[keys[i]].options && dao.daoFactory.associations[keys[i]].options.onUpdate && dao.daoFactory.associations[keys[i]].options.onUpdate === "restrict") {
restrict = true
}
}
}
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('success', results[1])
emitter.emit('sql', sql)
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('error', err)
emitter.emit('sql', sql)
})
.on('sql', function(sql) {
emitter.emit('sql', sql)
})
}).run()
}
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier)
return queryAndEmit.call(this, sql, 'bulkUpdate')
var self = this
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkUpdate'])
return chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
}
QueryInterface.prototype.delete = function(dao, tableName, identifier) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier)
return queryAndEmit.call(this, [sql, dao], 'delete')
var self = this
, restrict = false
, sql = self.QueryGenerator.deleteQuery(tableName, identifier)
// Check for a restrict field
if (!!dao.daoFactory && !!dao.daoFactory.associations) {
var keys = Object.keys(dao.daoFactory.associations)
, length = keys.length
for (var i = 0; i < length; i++) {
if (dao.daoFactory.associations[keys[i]].options && dao.daoFactory.associations[keys[i]].options.onDelete && dao.daoFactory.associations[keys[i]].options.onDelete === "restrict") {
restrict = true
}
}
}
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
}
QueryInterface.prototype.bulkDelete = function(tableName, identifier, options) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null}))
return queryAndEmit.call(this, sql, 'bulkDelete')
var self = this
var sql = self.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null}))
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkDelete', options])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('success', results[1])
})
.error(function(err) {
emitter.query = { sql: sql }
emitter.emit('sql', sql)
emitter.emit('error', err)
})
}).run()
}
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
......@@ -385,9 +490,7 @@ module.exports = (function() {
return this.QueryGenerator.escape(value)
}
// private
var queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) {
var queryAndEmit = QueryInterface.prototype.queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) {
options = Utils._.extend({
success: function(){},
error: function(){}
......
......@@ -14,6 +14,7 @@ module.exports = (function() {
@param {String} [password=null] The password which is used to authenticate against the database.
@param {Object} [options={}] An object with options.
@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 {Integer} [options.port=3306] The port of the relational database.
@param {String} [options.protocol='tcp'] The protocol of the relational database.
......@@ -68,6 +69,7 @@ module.exports = (function() {
this.options = Utils._.extend({
dialect: 'mysql',
dialectModulePath: null,
host: 'localhost',
protocol: 'tcp',
define: {},
......@@ -99,10 +101,15 @@ module.exports = (function() {
queue : this.options.queue,
native : this.options.native,
replication: this.options.replication,
dialectModulePath: this.options.dialectModulePath,
maxConcurrentQueries: this.options.maxConcurrentQueries
}
try {
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
} catch(err) {
throw new Error("The dialect " + this.options.dialect + " is not supported.")
}
this.daoFactoryManager = new DAOFactoryManager(this)
this.connectorManager = new ConnectorManager(this, this.config)
......@@ -184,6 +191,7 @@ module.exports = (function() {
var factory = new DAOFactory(daoName, attributes, options)
this.daoFactoryManager.addDAO(factory.init(this.daoFactoryManager))
return factory
}
......
var SqlString = exports;
var moment = require("moment")
, SqlString = exports;
SqlString.escapeId = function (val, forbidQualified) {
if (forbidQualified) {
......@@ -7,18 +8,36 @@ SqlString.escapeId = function (val, forbidQualified) {
return '`' + val.replace(/`/g, '``').replace(/\./g, '`.`') + '`';
};
SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
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) {
return 'NULL';
}
switch (typeof val) {
case 'boolean': return (val) ? 'true' : 'false';
case 'number': return val+'';
case 'boolean':
// SQLite doesn't have true/false support. MySQL aliases true/false to 1/0
// for us. Postgres actually has a boolean type with true/false literals,
// but sequelize doesn't use it yet.
return dialect === 'sqlite' ? +!!val : ('' + !!val);
case 'number':
return val+'';
}
if (val instanceof Date) {
val = SqlString.dateToString(val, timeZone || "Z");
val = SqlString.dateToString(val, timeZone || "Z", dialect);
}
if (Buffer.isBuffer(val)) {
......@@ -26,7 +45,7 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
}
if (Array.isArray(val)) {
return SqlString.arrayToList(val, timeZone);
return SqlString.arrayToList(val, timeZone, dialect, field);
}
if (typeof val === 'object') {
......@@ -37,8 +56,9 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
}
}
if (dialect == "postgres") {
if (dialect === 'postgres' || dialect === 'sqlite') {
// http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS
// http://stackoverflow.com/q/603572/130598
val = val.replace(/'/g, "''");
} else {
val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) {
......@@ -56,11 +76,22 @@ SqlString.escape = function(val, stringifyObjects, timeZone, dialect) {
return "'"+val+"'";
};
SqlString.arrayToList = function(array, timeZone) {
SqlString.arrayToList = function(array, timeZone, dialect, field) {
if (dialect === 'postgres') {
var ret = 'ARRAY[' + array.map(function(v) {
return SqlString.escape(v, true, timeZone, dialect, field);
}).join(',') + ']';
if (!!field && !!field.type) {
ret += '::' + field.type.replace(/\(\d+\)/g, '');
}
return ret;
} else {
return array.map(function(v) {
if (Array.isArray(v)) return '(' + SqlString.arrayToList(v) + ')';
return SqlString.escape(v, true, timeZone);
if (Array.isArray(v))
return '(' + SqlString.arrayToList(v, timeZone, dialect) + ')';
return SqlString.escape(v, true, timeZone, dialect);
}).join(', ');
}
};
SqlString.format = function(sql, values, timeZone, dialect) {
......@@ -75,10 +106,15 @@ SqlString.format = function(sql, values, timeZone, dialect) {
});
};
SqlString.dateToString = function(date, timeZone) {
SqlString.dateToString = function(date, timeZone, dialect) {
var dt = new Date(date);
if (timeZone != 'local') {
// TODO: Ideally all dialects would work a bit more like this
if (dialect === "postgres") {
return moment(dt).zone('+00:00').format("YYYY-MM-DD HH:mm:ss.SSS Z");
}
if (timeZone !== 'local') {
var tz = convertTimezone(timeZone);
dt.setTime(dt.getTime() + (dt.getTimezoneOffset() * 60000));
......@@ -87,14 +123,7 @@ SqlString.dateToString = function(date, timeZone) {
}
}
var year = dt.getFullYear();
var month = zeroPad(dt.getMonth() + 1);
var day = zeroPad(dt.getDate());
var hour = zeroPad(dt.getHours());
var minute = zeroPad(dt.getMinutes());
var second = zeroPad(dt.getSeconds());
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second;
return moment(dt).format("YYYY-MM-DD HH:mm:ss");
};
SqlString.bufferToString = function(buffer) {
......
var util = require("util")
, DataTypes = require("./data-types")
, SqlString = require("./sql-string")
, lodash = require("lodash")
, _string = require('underscore.string')
var Utils = module.exports = {
_: (function() {
var _ = require("lodash")
, _s = require('underscore.string')
var _ = lodash
, _s = _string
_.mixin(_s.exports())
_.mixin({
......@@ -39,19 +41,216 @@ var Utils = module.exports = {
var timeZone = null;
return SqlString.format(arr.shift(), arr, timeZone, dialect)
},
isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj);
// 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 (where[i] === null) {
// skip nulls
}
else 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))
},
pad: function (s) {
return s < 10 ? '0' + s : s
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 ''
}
},
toSqlDate: function(date) {
return date.getUTCFullYear() + '-' +
this.pad(date.getUTCMonth()+1) + '-' +
this.pad(date.getUTCDate()) + ' ' +
this.pad(date.getUTCHours()) + ':' +
this.pad(date.getUTCMinutes()) + ':' +
this.pad(date.getUTCSeconds())
isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj);
},
argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length == Object.keys(primaryKeys).length)
......@@ -181,12 +380,9 @@ var Utils = module.exports = {
removeTicks: function(s, tickChar) {
tickChar = tickChar || Utils.TICK_CHAR
return s.replace(new RegExp(tickChar, 'g'), "")
},
escape: function(s) {
return SqlString.escape(s, true, "local").replace(/\\"/g, '"')
}
}
Utils.CustomEventEmitter = require("./emitters/custom-event-emitter")
Utils.QueryChainer = require("./query-chainer")
Utils.CustomEventEmitter = require(__dirname + "/emitters/custom-event-emitter")
Utils.QueryChainer = require(__dirname + "/query-chainer")
Utils.Lingo = require("lingo")
......@@ -22,6 +22,10 @@
"janzeh@gmail.com",
"jmei@itu.dk"
]
},
{
"name": "Daniel Durante",
"email": "me@danieldurante.com"
}
],
"repository": {
......@@ -32,25 +36,28 @@
"url": "https://github.com/sequelize/sequelize/issues"
},
"dependencies": {
"lodash": "~1.2.1",
"lodash": "~1.3.1",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "1.1.1",
"moment": "~1.7.0",
"commander": "~0.6.0",
"dottie": "0.0.6-1",
"toposort-class": "0.1.4",
"validator": "~1.3.0",
"moment": "~2.1.0",
"commander": "~1.3.0",
"dottie": "0.0.8-0",
"toposort-class": "~0.2.0",
"generic-pool": "2.0.3",
"promise": "~3.0.0"
"promise": "~3.2.0"
},
"devDependencies": {
"jasmine-node": "1.5.0",
"sqlite3": "~2.1.5",
"mysql": "~2.0.0-alpha7",
"pg": "~2.0.0",
"sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha8",
"pg": "~2.2.0",
"buster": "~0.6.3",
"watchr": "~2.2.0",
"yuidocjs": "~0.3.36"
"watchr": "~2.4.3",
"yuidocjs": "~0.3.36",
"chai": "~1.7.2",
"mocha": "~1.12.0",
"chai-datetime": "~1.0.0",
"sinon": "~1.7.3"
},
"keywords": [
"mysql",
......@@ -60,14 +67,7 @@
],
"main": "index",
"scripts": {
"test": "npm run test-jasmine && 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-travis": "buster-test",
"test-buster-mysql": "DIALECT=mysql buster-test",
"test-buster-postgres": "DIALECT=postgres buster-test",
"test-buster-postgres-native": "DIALECT=postgres-native buster-test",
"test-buster-sqlite": "DIALECT=sqlite buster-test",
"test": "make all",
"docs": "node_modules/.bin/yuidoc . -o docs"
},
"bin": {
......
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')
}
}
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()
})
})
})
/*
// at time of writing (v1.6.0) Sequelize does not seem to support 'offset' on it's own consistently (goes wrong for PostGRES and SQLite)
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) {
user
.updateAttributes({name: 'foobar'})
.on('sql', function(sql) {
expect(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() {
// })
// })
// })
var Utils = require('../lib/utils')
describe('Utils', function() {
describe('removeCommentsFromFunctionString', function() {
it("removes line comments at the start of a line", function() {
var functionWithLineComments = function() {
// noot noot
}
var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString())
expect(result).toNotMatch(/.*noot.*/)
})
it("removes lines comments in the middle of a line", function() {
var functionWithLineComments = function() {
alert(1) // noot noot
}
var result = Utils.removeCommentsFromFunctionString(functionWithLineComments.toString())
expect(result).toNotMatch(/.*noot.*/)
})
it("removes range comments", function() {
var s = function() {
alert(1) /*
noot noot
*/
alert(2) /*
foo
*/
}.toString()
var result = Utils.removeCommentsFromFunctionString(s)
expect(result).toNotMatch(/.*noot.*/)
expect(result).toNotMatch(/.*foo.*/)
expect(result).toMatch(/.*alert\(2\).*/)
})
})
describe('argsArePrimaryKeys', function() {
it("doesn't detect primary keys if primareyKeys and values have different lengths", function() {
expect(Utils.argsArePrimaryKeys([1,2,3], [1])).toBeFalsy()
})
it("doesn't detect primary keys if primary keys are hashes or arrays", function() {
expect(Utils.argsArePrimaryKeys([[]], [1])).toBeFalsy()
})
it('detects primary keys if length is correct and data types are matching', function() {
expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).toBeTruthy()
})
it("detects primary keys if primary keys are dates and lengths are matching", function() {
expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).toBeTruthy()
})
})
describe('underscore', function() {
describe('underscoredIf', function() {
it('is defined', function() {
expect(Utils._.underscoredIf).toBeDefined()
})
it('underscores if second param is true', function() {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar')
})
it("doesn't underscore if second param is false", function() {
expect(Utils._.underscoredIf('fooBar', false)).toEqual('fooBar')
})
})
describe('camelizeIf', function() {
it('is defined', function() {
expect(Utils._.camelizeIf).toBeDefined()
})
it('camelizes if second param is true', function() {
expect(Utils._.camelizeIf('foo_bar', true)).toEqual('fooBar')
})
it("doesn't camelize if second param is false", function() {
expect(Utils._.underscoredIf('fooBar', true)).toEqual('foo_bar')
})
})
})
describe('isHash', function() {
it('doesn\'t match arrays', function() {
expect(Utils.isHash([])).toBeFalsy();
});
it('doesn\'t match null', function() {
expect(Utils.isHash(null)).toBeFalsy();
});
it('matches plain objects', function() {
var values = {
'name': {
'first': 'Foo',
'last': 'Bar'
}
};
expect(Utils.isHash(values)).toBeTruthy();
});
it('matches plain objects with length property/key', function() {
var values = {
'name': {
'first': 'Foo',
'last': 'Bar'
},
'length': 1
};
expect(Utils.isHash(values)).toBeTruthy();
});
});
describe('format', function() {
it('should format where clause correctly when the value is truthy', function() {
var where = ['foo = ?', 1];
expect(Utils.format(where)).toEqual('foo = 1');
});
it('should format where clause correctly when the value is falsy', function() {
var where = ['foo = ?', 0];
expect(Utils.format(where)).toEqual('foo = 0');
});
});
})
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), {
name: DataTypes.STRING
})
}
\ No newline at end of file
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe(Helpers.getTestDialectTeaser("BelongsTo"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('setAssociation', function() {
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).not.toEqual(null)
task.setUserXYZ(null).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).toEqual(null)
done()
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onDelete: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onDelete: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
Task.belongsTo(User, {onUpdate: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
Task.belongsTo(User, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
}
buster.spec.expose()
buster.testRunner.timeout = 500
describe(Helpers.getTestDialectTeaser("HasMany"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
})
describe('(1:N)', function() {
describe('hasSingle', function() {
before(function(done) {
this.Article = this.sequelize.define('Article', { 'title': Sequelize.STRING })
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING })
this.Article.hasMany(this.Label)
this.sequelize.sync({ force: true }).success(done)
})
it('does not have any labels assigned to it initially', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
var chainer = new Sequelize.Utils.QueryChainer([
article.hasLabel(label1),
article.hasLabel(label2)
])
chainer.run().success(function(_, hasLabel1, hasLabel2) {
expect(hasLabel1).toBeFalse()
expect(hasLabel2).toBeFalse()
done()
})
})
})
it('answers true if the label has been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
var chainer = new Sequelize.Utils.QueryChainer([
[ article, 'addLabel', [ label1 ]],
[ article, 'hasLabel', [ label1 ]],
[ article, 'hasLabel', [ label2 ]]
])
chainer.runSerially().success(function(_, label1, hasLabel1, hasLabel2) {
expect(hasLabel1).toBeTrue()
expect(hasLabel2).toBeFalse()
done()
})
})
})
})
describe('hasAll', function() {
before(function(done) {
this.Article = this.sequelize.define('Article', { 'title': Sequelize.STRING })
this.Label = this.sequelize.define('Label', { 'text': Sequelize.STRING })
this.Article.hasMany(this.Label)
this.sequelize.sync({ force: true }).success(done)
})
it('answers false if only some labels have been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
article.addLabel(label1).success(function() {
article.hasLabels([label1, label2]).success(function(result) {
expect(result).toBeFalse()
done()
})
})
})
})
it('answers true if all label have been assigned', function(done) {
var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }),
this.Label.create({ text: 'Epicness' })
])
chainer.run().success(function(results, article, label1, label2) {
article.setLabels([label1, label2]).success(function() {
article.hasLabels([label1, label2]).success(function(result) {
expect(result).toBeTrue()
done()
})
})
})
})
})
describe('setAssociations', function() {
it("clears associations when passing null to the set-method", function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
})
it("clears associations when passing null to the set-method with omitNull set to true", function(done) {
this.sequelize.options.omitNull = true
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
describe("getting assocations with options", function() {
before(function(done) {
var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
this.User.hasMany(self.Task)
this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false})
])
chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(done)
})
})
})
it("gets all associated objects when no options are passed", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2)
done()
})
})
})
it("only get objects that fulfill the options", function(done) {
this.User.find({ where: { username: 'John' } }).success(function (john) {
john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' }).success(function (tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.sequelize.sync({force: true}).success(done)
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECt, only UPDAET
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECT, only UPDATE
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
})
describe('(N:M)', function() {
describe("getting assocations with options", function() {
before(function(done) {
var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
self.User.hasMany(self.Task)
self.Task.hasMany(self.User)
this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false})
])
chainer.run().success(function (results, john, task1, task2) {
john.setTasks([task1, task2]).success(done)
})
})
})
it("gets all associated objects when no options are passed", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2)
done()
})
})
})
it("only get objects that fulfill the options", function(done) {
this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: {active: true}}).success(function (tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
it("removes the reference id, which was added in the first place", function() {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
expect(Task.attributes.UserId).toBeDefined()
Task.hasMany(User)
expect(Task.attributes.UserId).not.toBeDefined()
})
it("adds three items to the query chainer when calling sync", function() {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
Task.hasMany(User)
var add = this.spy()
this.stub(Sequelize.Utils, 'QueryChainer').returns({ add: add, runSerially: function(){} })
this.sequelize.sync({ force: true })
expect(add).toHaveBeenCalledThrice()
})
describe('setAssociations', function() {
it("clears associations when passing null to the set-method", function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasMany(Task)
Task.hasMany(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUsers([ user ]).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(1)
task.setUsers(null).success(function() {
task.getUsers().success(function(_users) {
expect(_users.length).toEqual(0)
done()
})
})
})
})
})
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
it('uses one insert into statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("INSERT INTO")
expect(sql).toMatch("VALUES (1,1),(1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one delete from statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("DELETE FROM")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for DELETE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
describe('join table creation', function () {
before(function (done) {
this.User = this.sequelize.define('User',
{ username: Sequelize.STRING },
{ tableName: 'users'}
)
this.Task = this.sequelize.define('Task',
{ title: Sequelize.STRING },
{ tableName: 'tasks' }
)
this.User.hasMany(this.Task,
{ joinTableName: 'user_has_tasks' }
)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
it('uses the specified joinTableName or a reasonable default', function(done) {
for (var associationName in this.User.associations) {
expect(associationName).not.toEqual(this.User.tableName)
expect(associationName).not.toEqual(this.Task.tableName)
var joinTableName = this.User.associations[associationName].options.joinTableName
if (typeof joinTableName !== 'undefined') {
expect(joinTableName).toEqual(associationName)
}
var tableName = this.User.associations[associationName].options.tableName
if (typeof tableName !== 'undefined') {
expect(tableName).toEqual(associationName)
}
}
done()
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onDelete: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onDelete: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasMany(Task, {onUpdate: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTasks([task]).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasMany(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../../index")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1500
describe(Helpers.getTestDialectTeaser("HasOne"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { self.sequelize = sequelize },
onComplete: done
})
})
describe('setAssociation', function() {
it('//clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).not.toEqual(null)
user.setTaskXYZ(null).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).toEqual(null)
done()
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
expect(tasks[0].UserId).toEqual(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks.length).toEqual(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
, Task = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasOne(Task, { foreignKey: 'userId', keyType: dataType })
self.sequelize.sync({ force: true }).success(function() {
expect(Task.rawAttributes.userId.type.toString())
.toEqual(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Mixin"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('getAssociation', function() {
it('returns the respective part of the association for 1:1 associations', function() {
var User = this.sequelize.define('User', {})
var Task = this.sequelize.define('Task', {})
User.hasOne(Task)
Task.belongsTo(User)
expect(User.getAssociation(Task).target).toEqual(Task)
})
})
})
var config = module.exports
config["node tests"] = {
environment: "node",
rootPath: "../",
tests: [
"spec/**/*.spec.js"
]
}
var Factories = module.exports = function(helpers) {
this.helpers = helpers
this.sequelize = this.helpers.sequelize
}
Factories.prototype.DAO = function(daoName, options, callback, count) {
count = count || 1
var self = this
, daos = []
this.helpers.async(function(done) {
var DAO = self.sequelize.daoFactoryManager.getDAO(daoName)
var create = function(cb) {
DAO.create(options).on('success', function(dao) {
daos.push(dao)
cb && cb()
}).on('error', function(err) {
console.log(err)
done()
})
}
var cb = function() {
if(--count) {
create(cb)
} else {
done()
callback && callback(daos)
}
}
create(cb)
})
}
Factories.prototype.User = function(options, callback, count) {
this.DAO('User', options, callback, count)
}
Sequelize = require("../../index")
var Helpers = module.exports = function(sequelize) {
this.sequelize = sequelize
this.Factories = new (require("./factories"))(this)
}
Helpers.prototype.sync = function() {
var self = this
this.async(function(done) {
self.sequelize
.sync({force: true})
.success(done)
.failure(function(err) { console.log(err) })
})
}
Helpers.prototype.drop = function() {
var self = this
this.async(function(done) {
self.sequelize
.drop()
.on('success', done)
.on('error', function(err) { console.log(err) })
})
}
Helpers.prototype.dropAllTables = function() {
var self = this
this.async(function(done) {
self.sequelize
.getQueryInterface()
.dropAllTables()
.success(done)
.error(function(err) { console.log(err) })
})
}
Helpers.prototype.async = function(fct) {
var done = false
runs(function() {
fct(function() { return done = true })
})
waitsFor(function(){ return done })
}
if(typeof require === 'function') {
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index')
describe(Helpers.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it('should give us an error for not having the correct server details', function(done) {
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster!
done()
}
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
d.add(sequelize.query)
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to find MySQL server. Please double check your settings.'
}
expect(err.message).toEqual(msg)
done()
})
d.run(function(){
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('should give us an error for not having the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") {
// This dialect doesn't support incorrect login information
expect('').toEqual('') // Silence Buster
return done()
}
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster!
done()
}
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
d.add(sequelize.query)
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to authenticate for MySQL. Please double check your settings.'
}
expect(err.message).toEqual(msg)
done()
})
d.run(function(){
sequelize.query('select 1 as hello')
.success(function(){})
})
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
var config = sequelize.config
var options = sequelize.options
expect(options.dialect).toEqual('mysql')
expect(config.database).toEqual('dbname')
expect(config.host).toEqual('example.com')
expect(config.username).toEqual('user')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(9821)
})
it('should work with no authentication options', function() {
var sequelize = new Sequelize('mysql://example.com:9821/dbname')
var config = sequelize.config
expect(config.username).toEqual(undefined)
expect(config.password).toEqual(null)
})
it('should use the default port when no other is specified', function() {
var sequelize = new Sequelize('mysql://example.com/dbname')
var config = sequelize.config
// The default port should be set
expect(config.port).toEqual(3306)
})
})
describe('Intantiation with arguments', function() {
it('should accept two parameters (database, username)', function() {
var sequelize = new Sequelize('dbname', 'root')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
})
it('should accept three parameters (database, username, password)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
})
it('should accept four parameters (database, username, password, options)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(999)
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, _ = require('lodash')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
this.sequelize = sequelize
this.User = sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
})
}.bind(this),
onComplete: function() {
this.User.sync({ force: true }).success(done)
}.bind(this)
})
})
describe('constructor', function() {
it("uses the passed dao name as tablename if freezeTableName", function() {
var User = this.sequelize.define('FrozenUser', {}, { freezeTableName: true })
expect(User.tableName).toEqual('FrozenUser')
})
it("uses the pluralized dao name as tablename unless freezeTableName", function() {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
expect(User.tableName).toEqual('SuperUsers')
})
it("uses checks to make sure dao factory isnt leaking on multiple define", function() {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize = this.sequelize.daoFactoryManager.all.length
var User2 = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize2 = this.sequelize.daoFactoryManager.all.length
expect(factorySize).toEqual(factorySize2)
})
it("attaches class and instance methods", function() {
var User = this.sequelize.define('UserWithClassAndInstanceMethods', {}, {
classMethods: { doSmth: function(){ return 1 } },
instanceMethods: { makeItSo: function(){ return 2}}
})
expect(User.doSmth).toBeDefined()
expect(User.doSmth()).toEqual(1)
expect(User.makeItSo).not.toBeDefined()
expect(User.build().doSmth).not.toBeDefined()
expect(User.build().makeItSo).toBeDefined()
expect(User.build().makeItSo()).toEqual(2)
})
it("throws an error if 2 autoIncrements are passed", function() {
Helpers.assertException(function() {
this.sequelize.define('UserWithTwoAutoIncrements', {
userid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
userscore: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }
})
}.bind(this), 'Invalid DAO definition. Only one autoincrement field allowed.')
})
it('throws an error if a custom model-wide validation is not a function', function() {
Helpers.assertException(function() {
this.sequelize.define('Foo', {
field: {
type: Sequelize.INTEGER
}
}, {
validate: {
notFunction: 33
}
})
}.bind(this), 'Members of the validate option must be functions. Model: Foo, error with validate member notFunction')
})
it('throws an error if a custom model-wide validation has the same name as a field', function() {
Helpers.assertException(function() {
this.sequelize.define('Foo', {
field: {
type: Sequelize.INTEGER
}
}, {
validate: {
field: function() {}
}
})
}.bind(this), 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field')
})
})
describe('build', function() {
it("doesn't create database entries", function(done) {
this.User.build({ username: 'John Wayne' })
this.User.all().success(function(users) {
expect(users.length).toEqual(0)
done()
})
})
it("fills the objects with default values", function() {
var Task = this.sequelize.define('Task', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
})
expect(Task.build().title).toEqual('a task!')
expect(Task.build().foo).toEqual(2)
expect(Task.build().bar).toEqual(undefined)
expect(Task.build().foobar).toEqual('asd')
expect(Task.build().flag).toEqual(false)
})
it("fills the objects with default values", function() {
var Task = this.sequelize.define('Task', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}, { timestamps: false })
expect(Task.build().title).toEqual('a task!')
expect(Task.build().foo).toEqual(2)
expect(Task.build().bar).toEqual(undefined)
expect(Task.build().foobar).toEqual('asd')
expect(Task.build().flag).toEqual(false)
})
it("stores the the passed values in a special variable", function() {
var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).toEqual({ username: 'John Wayne' })
})
it("attaches getter and setter methods from attribute definition", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters1', {
price: {
type: Sequelize.INTEGER,
get : function() {
return 'answer = ' + this.getDataValue('price');
},
set : function(v) {
return this.setDataValue('price', v + 42);
}
}
},{
});
expect(Product.build({price: 42}).price).toEqual('answer = 84');
var p = Product.build({price: 1});
expect(p.price).toEqual('answer = 43');
p.price = 0;
expect(p.price).toEqual('answer = 42'); // ah finally the right answer :-)
})
it("attaches getter and setter methods from options", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters2', {
priceInCents: {
type: Sequelize.INTEGER
}
},{
setterMethods: {
price: function(value) {
this.dataValues.priceInCents = value * 100;
}
},
getterMethods: {
price: function() {
return '$' + (this.getDataValue('priceInCents') / 100);
},
priceInCents: function() {
return this.dataValues.priceInCents;
}
}
});
expect(Product.build({price: 20}).priceInCents).toEqual(20 * 100);
expect(Product.build({priceInCents: 30 * 100}).price).toEqual('$' + 30);
})
it("attaches getter and setter methods from options only if not defined in attribute", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters3', {
price1: {
type: Sequelize.INTEGER,
set : function(v) { this.setDataValue('price1', v * 10); }
},
price2: {
type: Sequelize.INTEGER,
get : function(v) { return this.getDataValue('price2') * 10; }
}
},{
setterMethods: {
price1: function(v) { this.setDataValue('price1', v * 100); }
},
getterMethods: {
price2: function() { return '$' + this.getDataValue('price2'); }
}
});
var p = Product.build({ price1: 1, price2: 2 });
expect(p.price1).toEqual(10);
expect(p.price2).toEqual(20);
})
})
describe('findOrCreate', function () {
it("Returns instace if already existent. Single find field.", function (done) {
var self = this,
data = {
username: 'Username'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate({
username: user.username
}).success(function (_user, created) {
expect(_user.id).toEqual(user.id)
expect(_user.username).toEqual('Username')
expect(created).toBeFalse()
done()
})
})
})
it("Returns instace if already existent. Multiple find fields.", function (done) {
var self = this,
data = {
username: 'Username',
data: 'ThisIsData'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate(data).success(function (_user, created) {
expect(_user.id).toEqual(user.id)
expect(_user.username).toEqual('Username')
expect(_user.data).toEqual('ThisIsData')
expect(created).toBeFalse()
done()
})
})
})
it("creates new instance with default value.", function (done) {
var self = this,
data = {
username: 'Username'
},
default_values = {
data: 'ThisIsData'
};
this.User.findOrCreate(data, default_values).success(function (user, created) {
expect(user.username).toEqual('Username')
expect(user.data).toEqual('ThisIsData')
expect(created).toBeTrue()
done()
})
})
})
describe('create', function() {
it("casts empty arrays correctly for postgresql", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').toEqual('')
return done()
}
var User = this.sequelize.define('UserWithArray', {
myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
})
User.sync({force: true}).success(function() {
User.create({myvals: [], mystr: []}).on('sql', function(sql){
expect(sql.indexOf('ARRAY[]::INTEGER[]')).toBeGreaterThan(-1)
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).toBeGreaterThan(-1)
done()
})
})
})
it("doesn't allow duplicated records with unique:true", function(done) {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: true }
})
User.sync({ force: true }).success(function() {
User.create({ username:'foo' }).success(function() {
User.create({ username: 'foo' }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: /.*Duplicate\ entry.*/,
postgres: /.*duplicate\ key\ value.*/
})
done()
})
})
})
})
it("raises an error if created object breaks definition contraints", function(done) {
var User = this.sequelize.define('UserWithNonNullSmth', {
username: { type: Sequelize.STRING, unique: true },
smth: { type: Sequelize.STRING, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({ username: 'foo', smth: null }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: "Column 'smth' cannot be null",
postgres: /.*column "smth" violates not-null.*/
})
User.create({ username: 'foo', smth: 'foo' }).success(function() {
User.create({ username: 'foo', smth: 'bar' }).error(function(err) {
expect(err).toBeDefined()
Helpers.checkMatchForDialects(dialect, err.message, {
sqlite: /.*SQLITE_CONSTRAINT.*/,
mysql: "Duplicate entry 'foo' for key 'username'",
postgres: /.*duplicate key value violates unique constraint.*/
})
done()
})
})
})
})
})
it('raises an error if you mess up the datatype', function() {
Helpers.assertException(function() {
this.sequelize.define('UserBadDataType', {
activity_date: Sequelize.DATe
})
}.bind(this), 'Unrecognized data type for field activity_date')
Helpers.assertException(function() {
this.sequelize.define('UserBadDataType', {
activity_date: {type: Sequelize.DATe}
})
}.bind(this), 'Unrecognized data type for field activity_date')
})
it('sets a 64 bit int in bigint', function(done) {
var User = this.sequelize.define('UserWithBigIntFields', {
big: Sequelize.BIGINT
})
User.sync({ force: true }).success(function() {
User.create({ big: '9223372036854775807' }).on('success', function(user) {
expect(user.big).toBe( '9223372036854775807' )
done()
})
})
})
it('sets auto increment fields', function(done) {
var User = this.sequelize.define('UserWithAutoIncrementField', {
userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({}).on('success', function(user) {
expect(user.userid).toEqual(1)
User.create({}).on('success', function(user) {
expect(user.userid).toEqual(2)
done()
})
})
})
})
it('allows the usage of options as attribute', function(done) {
var User = this.sequelize.define('UserWithNameAndOptions', {
name: Sequelize.STRING,
options: Sequelize.TEXT
})
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
User.sync({ force: true }).success(function() {
User
.create({ name: 'John Doe', options: options })
.success(function(user) {
expect(user.options).toEqual(options)
done()
})
})
})
it('allows sql logging', function(done) {
var User = this.sequelize.define('UserWithUniqueNameAndNonNullSmth', {
name: {type: Sequelize.STRING, unique: true},
smth: {type: Sequelize.STRING, allowNull: false}
})
User.sync({ force: true }).success(function() {
User
.create({ name: 'Fluffy Bunny', smth: 'else' })
.on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("INSERT")).toBeGreaterThan(-1)
done()
})
})
})
it('should only store the values passed in the whitelist', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data, ['username']).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).toEqual(data.username)
expect(_user.secretValue).not.toEqual(data.secretValue)
expect(_user.secretValue).toEqual(null)
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).toEqual(data.username)
expect(_user.secretValue).toEqual(data.secretValue)
done()
})
})
})
it('can omitt autoincremental columns', function(done) {
var self = this
, data = { title: 'Iliad' }
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
dataTypes.forEach(function(dataType, index) {
var Book = self.sequelize.define('Book'+index, {
id: { type: dataType, primaryKey: true, autoIncrement: true },
title: Sequelize.TEXT
})
Book.sync({ force: true }).success(function() {
Book
.create(data)
.success(function(book) {
expect(book.title).toEqual(data.title)
expect(book.author).toEqual(data.author)
expect(Book.rawAttributes.id.type.toString())
.toEqual(dataTypes[index].toString())
Book.drop()
if (index >= dataTypes.length - 1) {
done()
}
})
})
})
})
it('saves data with single quote', function(done) {
var quote = "single'quote"
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).toEqual(quote, 'memory single quote')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(quote, 'SQL single quote')
done()
})
})
})
it('saves data with double quote', function(done) {
var quote = 'double"quote'
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).toEqual(quote, 'memory double quote')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(quote, 'SQL double quote')
done()
})
})
})
it('saves stringified JSON data', function(done) {
var json = JSON.stringify({ key: 'value' })
, self = this
this.User.create({ data: json }).success(function(user) {
expect(user.data).toEqual(json, 'memory data')
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).toEqual(json, 'SQL data')
done()
})
})
})
it('stores the current date in createdAt', function(done) {
this.User.create({ username: 'foo' }).success(function(user) {
expect(parseInt(+user.createdAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
it('allows setting custom IDs', function (done) {
this.User.create({ id: 42 }).success(function (user) {
expect(user.id).toEqual(42)
this.User.find(42).success(function (user) {
expect(user).toBeDefined()
done()
})
}.bind(this))
})
describe('enums', function() {
before(function(done) {
this.Item = this.sequelize.define('Item', {
state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }
})
this.sequelize.sync({ force: true }).success(function() {
this.Item.create({ state: 'available' }).success(function(item) {
this.item = item
done()
}.bind(this))
}.bind(this))
})
it('correctly restores enum values', function(done) {
this.Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.id).toEqual(this.item.id)
done()
}.bind(this))
})
})
})
describe('bulkCreate', function() {
it('inserts multiple values respecting the white list', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data, ['username']).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].secretValue).toBeNull();
expect(users[1].username).toEqual("Paul")
expect(users[1].secretValue).toBeNull();
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].secretValue).toEqual('42')
expect(users[1].username).toEqual("Paul")
expect(users[1].secretValue).toEqual('23')
done()
})
})
})
it('saves data with single quote', function(done) {
var self = this
, quote = "Single'Quote"
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(quote)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var self = this
, quote = 'Double"Quote'
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(quote)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var self = this
, json = JSON.stringify({ key: 'value' })
, data = [{ username: 'Peter', data: json},
{ username: 'Paul', data: json}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(users[0].data).toEqual(json)
expect(users[1].username).toEqual("Paul")
expect(users[1].data).toEqual(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
var self = this
, data = [{ username: 'Peter'},
{ username: 'Paul'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(2)
expect(users[0].username).toEqual("Peter")
expect(parseInt(+users[0].createdAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(users[1].username).toEqual("Paul")
expect(parseInt(+users[1].createdAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
describe('enums', function() {
before(function(done) {
this.Item = this.sequelize.define('Item', {
state: { type: Helpers.Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] },
name: Sequelize.STRING
})
this.sequelize.sync({ force: true }).success(function() {
this.Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() {
done()
}.bind(this))
}.bind(this))
})
it('correctly restores enum values', function(done) {
this.Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.name).toEqual('B')
done()
}.bind(this))
})
})
}) // - bulkCreate
describe('update', function() {
it('updates only values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
users.forEach(function (user) {
if (user.secretValue == '42') {
expect(user.username).toEqual("Bill")
} else {
expect(user.username).toEqual("Bob")
}
})
done()
})
})
})
})
it('sets updatedAt to the current timestamp', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
expect(users[0].username).toEqual("Bill")
expect(users[1].username).toEqual("Bill")
expect(users[2].username).toEqual("Bob")
expect(parseInt(+users[0].updatedAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(parseInt(+users[1].updatedAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
})
}) // - update
describe('destroy', function() {
it('deletes values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.destroy({secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("Bob")
done()
})
})
})
})
it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this
, User = this.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, {
paranoid: true
})
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
User.sync({ force: true }).success(function() {
User.bulkCreate(data).success(function() {
User.destroy({secretValue: '42'})
.success(function() {
User.findAll({order: 'id'}).success(function(users) {
expect(users.length).toEqual(3)
expect(users[0].username).toEqual("Peter")
expect(users[1].username).toEqual("Paul")
expect(users[2].username).toEqual("Bob")
expect(parseInt(+users[0].deletedAt/5000)).toEqual(parseInt(+new Date()/5000))
expect(parseInt(+users[1].deletedAt/5000)).toEqual(parseInt(+new Date()/5000))
done()
})
})
})
})
})
}) // - destroy
describe('special where conditions', function() {
before(function(done) {
var self = this
this.User.create({
username: 'boo',
intVal: 5,
theDate: '2013-01-01 12:00'
}).success(function(user){
self.user = user
self.User.create({
username: 'boo2',
intVal: 10,
theDate: '2013-01-10 12:00'
}).success(function(user2){
self.user2 = user2
done()
})
})
})
it('should be able to find a row between a certain date', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row between a certain date and an additional where clause', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
},
intVal: 10
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row not between a certain integer', function(done) {
this.User.findAll({
where: {
intVal: {
nbetween: [8, 10]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2012-12-10', '2013-01-02'],
nbetween: ['2013-01-04', '2013-01-20']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
between: [new Date('2012-12-10'), new Date('2013-01-02')],
nbetween: [new Date('2013-01-04'), new Date('2013-01-20')]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using greater than or equal to logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
gte: new Date('2013-01-09')
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than or equal to', function(done) {
this.User.find({
where: {
intVal: {
gte: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than', function(done) {
this.User.find({
where: {
intVal: {
gt: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using lesser than or equal to', function(done) {
this.User.find({
where: {
intVal: {
lte: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find a row using lesser than', function(done) {
this.User.find({
where: {
intVal: {
lt: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should have no problem finding a row using lesser and greater than', function(done) {
this.User.findAll({
where: {
intVal: {
lt: 6,
gt: 4
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not equal to logic', function(done) {
this.User.find({
where: {
intVal: {
ne: 10
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find multiple users with any of the special where logic properties', function(done) {
this.User.findAll({
where: {
intVal: {
lte: 10
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
expect(users[1].username).toEqual('boo2')
expect(users[1].intVal).toEqual(10)
done()
})
})
})
describe('find', function find() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.user = user
done()
}.bind(this))
})
it('returns a single dao', function(done) {
this.User.find(this.user.id).success(function(user) {
expect(Array.isArray(user)).toBeFalsy()
expect(user.id).toEqual(this.user.id)
expect(user.id).toEqual(1)
done()
}.bind(this))
})
it('returns a single dao given a string id', function(done) {
this.User.find(this.user.id + '').success(function(user) {
expect(Array.isArray(user)).toBeFalsy()
expect(user.id).toEqual(this.user.id)
expect(user.id).toEqual(1)
done()
}.bind(this))
})
it("should make aliased attributes available", function(done) {
this.User.find({
where: { id: 1 },
attributes: ['id', ['username', 'name']]
}).success(function(user) {
expect(user.name).toEqual('barfooz')
done()
})
})
it("should not try to convert boolean values if they are not selected", function (done) {
var UserWithBoolean = this.sequelize.define('user', {
active: Sequelize.BOOLEAN
})
this.sequelize.sync({force: true}).success(function () {
UserWithBoolean.create({ active: true }).success(function (user) {
UserWithBoolean.find({ where: { id: user.id }, attributes: [ 'id' ] }).success(function (user) {
expect(user.active).not.toBeDefined()
done()
})
})
})
})
it('finds a specific user via where option', function(done) {
this.User.find({ where: { username: 'barfooz' } }).success(function(user) {
expect(user.username).toEqual('barfooz')
done()
})
})
it("doesn't find a user if conditions are not matching", function(done) {
this.User.find({ where: { username: 'foo' } }).success(function(user) {
expect(user).toBeNull()
done()
})
})
it('allows sql logging', function(done) {
this.User.find({ where: { username: 'foo' } })
.on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
it('ignores passed limit option', function(done) {
this.User.find({ limit: 10 }).success(function(user) {
// it returns an object instead of an array
expect(Array.isArray(user)).toBeFalsy()
expect(user.hasOwnProperty('username')).toBeTruthy()
done()
})
})
it('finds entries via primary keys', function(done) {
var User = this.sequelize.define('UserWithPrimaryKey', {
identifier: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING
})
User.sync({ force: true }).success(function() {
User.create({
identifier: 'an identifier',
name: 'John'
}).success(function(u) {
expect(u.id).not.toBeDefined()
User.find('an identifier').success(function(u2) {
expect(u2.identifier).toEqual('an identifier')
expect(u2.name).toEqual('John')
done()
})
})
})
})
it('returns the selected fields as instance.selectedValues', function(done) {
this.User.create({
username: 'JohnXOXOXO'
}).success(function() {
this.User.find({
where: { username: 'JohnXOXOXO' },
attributes: ['username']
}).success(function(user) {
expect(user.selectedValues).toEqual({ username: 'JohnXOXOXO' })
done()
})
}.bind(this))
})
it('returns the selected fields and all fields of the included table as instance.selectedValues', function(done) {
this.Mission = this.sequelize.define('Mission', {
title: {type: Sequelize.STRING, defaultValue: 'a mission!!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
})
this.Mission.belongsTo(this.User)
this.User.hasMany(this.Mission)
this.sequelize.sync({ force: true }).complete(function() {
this.Mission.create()
.success(function(mission) {
this.User.create({
username: 'John DOE'
}).success(function(user) {
mission.setUser(user)
.success(function() {
this.User.find({
where: { username: 'John DOE' },
attributes: ['username'],
include: [this.Mission]
}).success(function(user) {
expect(user.selectedValues).toEqual({ username: 'John DOE' })
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('always honors ZERO as primary key', function(_done) {
var permutations = [
0,
'0',
{where: {id: 0}},
{where: {id: '0'}}
]
, done = _.after(2 * permutations.length, _done);
this.User.create({username: 'jack'}).success(function (jack) {
this.User.create({username: 'jill'}).success(function (jill) {
permutations.forEach(function(perm) {
this.User.find(perm).done(function(err, user) {
expect(err).toBeNull();
expect(user).toBeNull();
done();
}).on('sql', function(s) {
expect(s.indexOf(0)).not.toEqual(-1);
done();
})
}.bind(this))
}.bind(this))
}.bind(this))
})
describe('eager loading', function() {
before(function() {
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING })
this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING })
this.init = function(callback) {
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
callback()
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this)
})
describe('belongsTo', function() {
before(function(done) {
this.Task.belongsTo(this.Worker)
this.init(function() {
this.task.setWorker(this.worker).success(done)
}.bind(this))
})
it('throws an error about unexpected input if include contains a non-object', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ 1 ] })
}.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
})
it('throws an error about missing attributes if include contains an object with daoFactory', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Worker } ] })
}.bind(this), 'Include malformed. Expected attributes: daoFactory, as!')
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('returns the associated worker via task.worker', function(done) {
this.Task.find({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, task) {
expect(err).toBeNull()
expect(task).toBeDefined()
expect(task.worker).toBeDefined()
expect(task.worker.name).toEqual('worker')
done()
}.bind(this))
})
it('returns the private and public ip', function(done) {
var Domain = this.sequelize.define('Domain', { ip: Sequelize.STRING })
var Environment = this.sequelize.define('Environment', { name: Sequelize.STRING })
Environment
.belongsTo(Domain, { as: 'PrivateDomain', foreignKey: 'privateDomainId' })
.belongsTo(Domain, { as: 'PublicDomain', foreignKey: 'publicDomainId' })
this.sequelize.sync({ force: true }).complete(function() {
Domain.create({ ip: '192.168.0.1' }).success(function(privateIp) {
Domain.create({ ip: '91.65.189.19' }).success(function(publicIp) {
Environment.create({ name: 'environment' }).success(function(env) {
env.setPrivateDomain(privateIp).success(function() {
env.setPublicDomain(publicIp).success(function() {
Environment.find({
where: { name: 'environment' },
include: [
{ daoFactory: Domain, as: 'PrivateDomain' },
{ daoFactory: Domain, as: 'PublicDomain' }
]
}).complete(function(err, environment) {
expect(err).toBeNull()
expect(environment).toBeDefined()
expect(environment.privateDomain).toBeDefined()
expect(environment.privateDomain.ip).toEqual('192.168.0.1')
expect(environment.publicDomain).toBeDefined()
expect(environment.publicDomain.ip).toEqual('91.65.189.19')
done()
})
})
})
})
})
})
})
})
})
describe('hasOne', function() {
before(function(done) {
this.Worker.hasOne(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTask(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.find({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.task).toBeDefined()
expect(worker.task.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasOne with alias', function() {
before(function(done) {
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDo(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.toDo).toBeDefined()
expect(worker.toDo.title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(worker.toDo.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany', function() {
before(function(done) {
this.Worker.hasMany(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTasks([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.find({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.tasks).toBeDefined()
expect(worker.tasks[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany with alias', function() {
before(function(done) {
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDos([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.find({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(err).toBeNull()
expect(worker).toBeDefined()
expect(worker.toDos).toBeDefined()
expect(worker.toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(worker.toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
})
})
describe('queryOptions', function() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.user = user
done()
}.bind(this))
})
it("should return a DAO when queryOptions are not set", function (done) {
this.User.find({ where: { username: 'barfooz'}}).done(function (err, user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
done();
}.bind(this))
})
it("should return a DAO when raw is false", function (done) {
this.User.find({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
done();
}.bind(this))
})
it("should return raw data when raw is true", function (done) {
this.User.find({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, user) {
expect(user).not.toHavePrototype(this.User.DAO.prototype)
expect(user).toBeObject()
done();
}.bind(this))
})
}) // - describe: queryOptions
}) //- describe: find
describe('findAll', function findAll() {
describe('eager loading', function() {
before(function() {
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING })
this.Worker = this.sequelize.define('Worker', { name: Sequelize.STRING })
})
describe('belongsTo', function() {
before(function(done) {
this.Task.belongsTo(this.Worker)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.task.setWorker(this.worker).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error about unexpected input if include contains a non-object', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ 1 ] })
}.bind(this), 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
})
it('throws an error about missing attributes if include contains an object with daoFactory', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Worker } ] })
}.bind(this), 'Include malformed. Expected attributes: daoFactory, as!')
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('returns the associated worker via task.worker', function(done) {
this.Task.findAll({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, tasks) {
expect(err).toBeNull()
expect(tasks).toBeDefined()
expect(tasks[0].worker).toBeDefined()
expect(tasks[0].worker.name).toEqual('worker')
done()
}.bind(this))
})
})
describe('hasOne', function() {
before(function(done) {
this.Worker.hasOne(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTask(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.findAll({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].task).toBeDefined()
expect(workers[0].task.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasOne with alias', function() {
before(function(done) {
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDo(this.task).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].toDo).toBeDefined()
expect(workers[0].toDo.title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(workers[0].toDo.title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany', function() {
before(function(done) {
this.Worker.hasMany(this.Task)
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setTasks([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not associated', function() {
Helpers.assertException(function() {
this.Task.findAll({ include: [ this.Worker ] })
}.bind(this), 'Worker is not associated to Task!')
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].tasks).toBeDefined()
expect(workers[0].tasks[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('hasMany with alias', function() {
before(function(done) {
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.sequelize.sync({ force: true }).complete(function() {
this.Worker.create({ name: 'worker' }).success(function(worker) {
this.Task.create({ title: 'homework' }).success(function(task) {
this.worker = worker
this.task = task
this.worker.setToDos([ this.task ]).success(done)
}.bind(this))
}.bind(this))
}.bind(this))
})
it('throws an error if included DaoFactory is not referenced by alias', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ this.Task ] })
}.bind(this), 'Task is not associated to Worker!')
})
it('throws an error if alias is not associated', function() {
Helpers.assertException(function() {
this.Worker.findAll({ include: [ { daoFactory: this.Task, as: 'Work' } ] })
}.bind(this), 'Task (Work) is not associated to Worker!')
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(err).toBeNull()
expect(workers).toBeDefined()
expect(workers[0].toDos).toBeDefined()
expect(workers[0].toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(workers[0].toDos[0].title).toEqual('homework')
done()
}.bind(this))
})
})
describe('queryOptions', function() {
before(function(done) {
this.User.create({
username: 'barfooz'
}).success(function(user) {
this.user = user
done()
}.bind(this))
})
it("should return a DAO when queryOptions are not set", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}).done(function (err, users) {
users.forEach(function (user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
}, this)
done();
}.bind(this))
})
it("should return a DAO when raw is false", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}, { raw: false }).done(function (err, users) {
users.forEach(function (user) {
expect(user).toHavePrototype(this.User.DAO.prototype)
}, this)
done();
}.bind(this))
})
it("should return raw data when raw is true", function (done) {
this.User.findAll({ where: { username: 'barfooz'}}, { raw: true }).done(function (err, users) {
users.forEach(function (user) {
expect(user).not.toHavePrototype(this.User.DAO.prototype)
expect(users[0]).toBeObject()
}, this)
done();
}.bind(this))
})
}) // - describe: queryOptions
})
}) //- describe: findAll
describe('min', function() {
before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function(){
this.UserWithDec.sync({ force: true }).success(done)
}.bind(this))
})
it("should return the min value", function(done) {
this.UserWithAge.create({ age: 2 }).success(function() {
this.UserWithAge.create({ age: 3 }).success(function() {
this.UserWithAge.min('age').success(function(min) {
expect(min).toEqual(2)
done()
})
}.bind(this))
}.bind(this))
})
it('allows sql logging', function(done) {
this.UserWithAge.min('age').on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
it("should allow decimals in min", function(done){
this.UserWithDec.create({value: 3.5}).success(function(){
this.UserWithDec.create({ value: 5.5 }).success(function(){
this.UserWithDec.min('value').success(function(min){
expect(min).toEqual(3.5)
done()
})
}.bind(this))
}.bind(this))
})
}) //- describe: min
describe('max', function() {
before(function(done) {
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER,
order: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function(){
this.UserWithDec.sync({ force: true }).success(done)
}.bind(this))
})
it("should return the max value for a field named the same as an SQL reserved keyword", function(done) {
this.UserWithAge.create({age: 3, order: 5}).success(function(){
this.UserWithAge.max('order').success(function(max) {
expect(max).toEqual(5)
done()
})
}.bind(this))
})
it("should return the max value", function(done) {
this.UserWithAge.create({ age: 2 }).success(function() {
this.UserWithAge.create({ age: 3 }).success(function() {
this.UserWithAge.max('age').success(function(max) {
expect(max).toEqual(3)
done()
})
}.bind(this))
}.bind(this))
})
it("should allow decimals in max", function(done){
this.UserWithDec.create({value: 3.5}).success(function(){
this.UserWithDec.create({ value: 5.5 }).success(function(){
this.UserWithDec.max('value').success(function(max){
expect(max).toEqual(5.5)
done()
})
}.bind(this))
}.bind(this))
})
it('allows sql logging', function(done) {
this.UserWithAge.max('age').on('sql', function(sql) {
expect(sql).toBeDefined()
expect(sql.toUpperCase().indexOf("SELECT")).toBeGreaterThan(-1)
done()
})
})
}) //- describe: max
describe('schematic support', function() {
before(function(done){
var self = this;
this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER
})
this.UserSpecial = this.sequelize.define('UserSpecial', {
age: Sequelize.INTEGER
})
self.sequelize.dropAllSchemas().success(function(){
self.sequelize.createSchema('schema_test').success(function(){
self.sequelize.createSchema('special').success(function(){
self.UserSpecial.schema('special').sync({force: true}).success(function(UserSpecialSync){
self.UserSpecialSync = UserSpecialSync;
done()
})
})
})
})
})
it("should be able to list schemas", function(done){
this.sequelize.showAllSchemas().success(function(schemas){
expect(schemas).toBeDefined()
expect(schemas[0]).toBeArray()
expect(schemas[0].length).toEqual(2)
done()
})
})
if (dialect === "mysql") {
it("should take schemaDelimiter into account if applicable", function(done){
var UserSpecialUnderscore = this.sequelize.define('UserSpecialUnderscore', {age: Sequelize.INTEGER}, {schema: 'hello', schemaDelimiter: '_'})
var UserSpecialDblUnderscore = this.sequelize.define('UserSpecialDblUnderscore', {age: Sequelize.INTEGER})
UserSpecialUnderscore.sync({force: true}).success(function(User){
UserSpecialDblUnderscore.schema('hello', '__').sync({force: true}).success(function(DblUser){
DblUser.create({age: 3}).on('sql', function(dblSql){
User.create({age: 3}).on('sql', function(sql){
expect(dblSql).toBeDefined()
expect(dblSql.indexOf('INSERT INTO `hello__UserSpecialDblUnderscores`')).toBeGreaterThan(-1)
expect(sql).toBeDefined()
expect(sql.indexOf('INSERT INTO `hello_UserSpecialUnderscores`')).toBeGreaterThan(-1)
done()
})
})
})
})
})
}
it("should be able to create and update records under any valid schematic", function(done){
var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){
expect(UserSpecial).toBeDefined()
expect(UserPublic).toBeDefined()
if (dialect === "postgres") {
expect(self.UserSpecialSync.getTableName()).toEqual('"special"."UserSpecials"');
expect(UserSpecial.indexOf('INSERT INTO "special"."UserSpecials"')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO "UserPublics"')).toBeGreaterThan(-1)
} else if (dialect === "sqlite") {
expect(self.UserSpecialSync.getTableName()).toEqual('`special`.`UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1)
} else {
expect(self.UserSpecialSync.getTableName()).toEqual('`special.UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).toBeGreaterThan(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).toBeGreaterThan(-1)
}
})
.success(function(UserSpecial){
UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){
expect(user).toBeDefined()
if (dialect === "postgres") {
expect(user.indexOf('UPDATE "special"."UserSpecials"')).toBeGreaterThan(-1)
} else {
expect(user.indexOf('UPDATE `special.UserSpecials`')).toBeGreaterThan(-1)
}
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
})
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("DAO"), function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1,20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
self.ParanoidUser = sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, {
paranoid: true
})
self.ParanoidUser.hasOne( self.ParanoidUser )
},
onComplete: function() {
self.User.sync({ force: true }).success(function(){
self.HistoryLog.sync({ force: true }).success(function(){
self.ParanoidUser.sync({force: true }).success(done)
})
})
}
})
})
describe('increment', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2);
done();
});
});
});
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(2);
done();
});
});
});
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).done(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).done(function (err, user3) {
user1.increment(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(3);
done();
});
});
});
});
});
});
it('should still work right with other concurrent increments', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
var _done = _.after(3, function () {
self.User.find(1).done(function (err, user2) {
expect(user2.aNumber).toEqual(6);
done();
})
});
user1.increment(['aNumber'], 2).done(_done);
user1.increment(['aNumber'], 2).done(_done);
user1.increment(['aNumber'], 2).done(_done);
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.increment({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2);
done();
});
});
});
});
});
describe('decrement', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement(['aNumber'], 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2);
done();
});
});
});
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement('aNumber', 2).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-2);
done();
});
});
});
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).done(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).done(function (err, user3) {
user1.decrement(['aNumber'], 2).done(function (err, user4) {
self.User.find(1).done(function (err, user5) {
expect(user5.aNumber).toBe(-1);
done();
});
});
});
});
});
});
it('should still work right with other concurrent increments', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
var _done = _.after(3, function () {
self.User.find(1).done(function (err, user2) {
expect(user2.aNumber).toEqual(-6);
done();
})
});
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).done(function (err, user1) {
user1.decrement({ 'aNumber': 1, 'bNumber': 2}).done(function (err, user2) {
self.User.find(1).done(function (err, user3) {
expect(user3.aNumber).toBe(-1);
expect(user3.bNumber).toBe(-2);
done();
});
});
});
});
});
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function (done) {
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
originalUser.updateAttributes({ username: 'Doe John' }).done(function () {
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser === updatedUser).toBeTrue()
done();
})
})
})
})
it("should update the values on all references to the DAO", function (done) {
var self = this
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
self.User.find(originalUser.id).done(function (err, updater) {
updater.updateAttributes({ username: 'Doe John' }).done(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).toEqual('John Doe')
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser.username).toEqual('Doe John')
expect(updatedUser.username).toEqual('Doe John')
done();
})
})
})
})
})
it("should update read only attributes as well (updatedAt)", function (done) {
var self = this
this.timeout = 2000;
this.User.create({ username: 'John Doe' }).done(function (err, originalUser) {
var originallyUpdatedAt = originalUser.updatedAt
// Wait for a second, so updatedAt will actually be different
setTimeout(function () {
self.User.find(originalUser.id).done(function (err, updater) {
updater.updateAttributes({ username: 'Doe John' }).done(function () {
originalUser.reload().done(function (err, updatedUser) {
expect(originalUser.updatedAt).toBeGreaterThan(originallyUpdatedAt)
expect(updatedUser.updatedAt).toBeGreaterThan(originallyUpdatedAt)
done();
})
})
})
}, 1000)
})
})
it("should update the associations as well", function(done) {
var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING })
, Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
this.sequelize.sync({ force: true }).success(function() {
Book.create({ title: 'A very old book' }).success(function(book) {
Page.create({ content: 'om nom nom' }).success(function(page) {
book.setPages([ page ]).success(function() {
Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).success(function(leBook) {
page.updateAttributes({ content: 'something totally different' }).success(function(page) {
expect(leBook.pages[0].content).toEqual('om nom nom')
expect(page.content).toEqual('something totally different')
leBook.reload().success(function(leBook) {
expect(leBook.pages[0].content).toEqual('something totally different')
expect(page.content).toEqual('something totally different')
done()
})
})
})
})
})
}.bind(this))
}.bind(this))
})
});
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function() {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt instanceof Date).toBeTrue()
})
it("should store the current date in touchedAt", function() {
this.useFakeTimers().tick(5000)
var user = this.User.build({ username: 'a user'})
expect(+user.touchedAt).toBe(5000)
})
})
describe('allowNull date', function() {
it('should be just "null" and not Date with Invalid Date', function(done) {
var self = this;
this.User.build({ username: 'a user'}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue).toBe(null)
done()
})
})
})
it('should be the same valid date when saving the date', function(done) {
var self = this;
var date = new Date();
this.User.build({ username: 'a user', dateAllowNullTrue: date}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue.toString()).toEqual(date.toString())
done()
})
})
})
})
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err, result) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).toBeNull()
expect(result).toBeDefined()
done()
})
})
})
describe('save', function() {
it('should fail a validation upon creating', function(done){
this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1);
done();
});
})
it('should fail a validation upon building', function(done){
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateCustom).toBeDefined()
expect(err.validateCustom).toBeArray()
expect(err.validateCustom[0]).toBeDefined()
expect(err.validateCustom[0]).toEqual('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done){
this.User.create({aNumber: 0}).success(function(user){
user.updateAttributes({validateTest: 'hello'}).error(function(err){
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeDefined()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1)
done()
})
})
})
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).toEqual(0)
done()
})
})
it('saves a record with no primary key', function(done){
this.HistoryLog.create({ someText: 'Some random text', aNumber: 3, aRandomId: 5 }).success(function(log) {
log.updateAttributes({ aNumber: 5 }).success(function(newLog){
expect(newLog.aNumber).toEqual(5)
done()
})
})
})
before(function(done) {
this.UserEager = this.sequelize.define('UserEagerLoadingSaves', {
username : Helpers.Sequelize.STRING,
age : Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.ProjectEager = this.sequelize.define('ProjectEagerLoadingSaves', {
title : Helpers.Sequelize.STRING,
overdue_days: Helpers.Sequelize.INTEGER
}, { timestamps: false })
this.UserEager.hasMany(this.ProjectEager, { as: 'Projects' })
this.ProjectEager.belongsTo(this.UserEager, { as: 'Poobah' })
this.sequelize.sync({ force: true }).success(done)
})
it('saves one object that has a collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'joe', age: 1 }).success(function(user) {
this.ProjectEager.create({ title: 'project-joe1', overdue_days: 0 }).success(function(project1) {
this.ProjectEager.create({ title: 'project-joe2', overdue_days: 0 }).success(function(project2) {
user.setProjects([project1, project2]).success(function() {
this.UserEager.find({where: {age: 1}, include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(user) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(1)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
user.age = user.age + 1; // happy birthday joe
user.save().success(function(saveduser) {
expect(user.username).toEqual('joe')
expect(user.age).toEqual(2)
expect(user.projects).toBeDefined()
expect(user.projects.length).toEqual(2)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each a have collection of eagerly loaded objects', function(done) {
this.UserEager.create({ username: 'bart', age: 20 }).success(function(bart) {
this.UserEager.create({ username: 'lisa', age: 20 }).success(function(lisa) {
this.ProjectEager.create({ title: 'detention1', overdue_days: 0 }).success(function(detention1) {
this.ProjectEager.create({ title: 'detention2', overdue_days: 0 }).success(function(detention2) {
this.ProjectEager.create({ title: 'exam1', overdue_days: 0 }).success(function(exam1) {
this.ProjectEager.create({ title: 'exam2', overdue_days: 0 }).success(function(exam2) {
bart.setProjects([detention1, detention2]).success(function() {
lisa.setProjects([exam1, exam2]).success(function() {
this.UserEager.findAll({where: {age: 20}, order: 'username ASC', include: [{model: this.ProjectEager, as: 'Projects'}]}).success(function(simpsons) {
var _bart, _lisa;
expect(simpsons.length).toEqual(2)
_bart = simpsons[0];
_lisa = simpsons[1];
expect(_bart.projects).toBeDefined()
expect(_lisa.projects).toBeDefined()
expect(_bart.projects.length).toEqual(2)
expect(_lisa.projects.length).toEqual(2)
_bart.age = _bart.age + 1; // happy birthday bart - off to Moe's
_bart.save().success(function(savedbart) {
expect(savedbart.username).toEqual('bart')
expect(savedbart.age).toEqual(21)
_lisa.username = 'lsimpson';
_lisa.save().success(function(savedlisa) {
expect(savedlisa.username).toEqual('lsimpson')
expect(savedlisa.age).toEqual(20)
done()
})
})
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it('saves many objects that each has one eagerly loaded object (to which they belong)', function(done) {
this.UserEager.create({ username: 'poobah', age: 18 }).success(function(user) {
this.ProjectEager.create({ title: 'homework', overdue_days: 10 }).success(function(homework) {
this.ProjectEager.create({ title: 'party', overdue_days: 2 }).success(function(party) {
user.setProjects([homework, party]).success(function() {
this.ProjectEager.findAll({include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(projects) {
expect(projects.length).toEqual(2)
expect(projects[0].poobah).toBeDefined()
expect(projects[1].poobah).toBeDefined()
expect(projects[0].poobah.username).toEqual('poobah')
expect(projects[1].poobah.username).toEqual('poobah')
projects[0].title = 'partymore';
projects[1].title = 'partymore';
projects[0].overdue_days = 0;
projects[1].overdue_days = 0;
projects[0].save().success(function() {
projects[1].save().success(function() {
this.ProjectEager.findAll({where: {title: 'partymore', overdue_days: 0}, include: [{model: this.UserEager, as: 'Poobah'}]}).success(function(savedprojects) {
expect(savedprojects.length).toEqual(2)
expect(savedprojects[0].poobah).toBeDefined()
expect(savedprojects[1].poobah).toBeDefined()
expect(savedprojects[0].poobah.username).toEqual('poobah')
expect(savedprojects[1].poobah.username).toEqual('poobah')
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('toJSON', function toJSON() {
before(function(done) {
this.User = this.sequelize.define('UserWithUsernameAndAgeAndIsAdmin', {
username: Helpers.Sequelize.STRING,
age: Helpers.Sequelize.INTEGER,
isAdmin: Helpers.Sequelize.BOOLEAN
}, { timestamps: false })
this.Project = this.sequelize.define('NiceProject', { title: Helpers.Sequelize.STRING }, { timestamps: false })
this.User.hasMany(this.Project, { as: 'Projects' })
this.Project.belongsTo(this.User, { as: 'LovelyUser' })
this.sequelize.sync({ force: true }).success(done)
})
it('returns an object containing all values', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(user.toJSON()).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
it('returns a response that can be stringified', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).toEqual('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
})
it('returns a response that can be stringified and then parsed', function() {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.parse(JSON.stringify(user))).toEqual({ username: 'test.user', age: 99, isAdmin: true, id: null })
})
it('includes the eagerly loaded associations', function(done) {
this.User.create({ username: 'fnord', age: 1, isAdmin: true }).success(function(user) {
this.Project.create({ title: 'fnord' }).success(function(project) {
user.setProjects([ project ]).success(function() {
this.User.findAll({include: [ { model: this.Project, as: 'Projects' } ]}).success(function(users) {
var _user = users[0]
expect(_user.projects).toBeDefined()
expect(JSON.parse(JSON.stringify(_user)).projects).toBeDefined()
this.Project.findAll({include: [ { model: this.User, as: 'LovelyUser' } ]}).success(function(projects) {
var _project = projects[0]
expect(_project.lovelyUser).toBeDefined()
expect(JSON.parse(JSON.stringify(_project)).lovelyUser).toBeDefined()
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('findAll', function findAll() {
it("escapes a single single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user'name" })
.success(function() {
self.User.findAll({
where: { username: "user'name" }
}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("user'name")
done()
})
})
})
it("escapes two single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user''name" })
.success(function() {
self.User.findAll({
where: { username: "user''name" }
}).success(function(users) {
expect(users.length).toEqual(1)
expect(users[0].username).toEqual("user''name")
done()
})
})
})
it("returns the timestamps if no attributes have been specified", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
this.User.findAll().success(function(users) {
expect(users[0].createdAt).toBeDefined()
done()
}.bind(this))
}.bind(this))
})
it("does not return the timestamps if the username attribute has been specified", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
this.User.findAll({ attributes: ['username'] }).success(function(users) {
expect(users[0].createdAt).not.toBeDefined()
expect(users[0].username).toBeDefined()
done()
}.bind(this))
}.bind(this))
})
it("creates the deletedAt property, when defining paranoid as true", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
expect(users[0].deletedAt).toBeDefined()
expect(users[0].deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
})
it("sets deletedAt property to a specific date when deleting an instance", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
users[0].destroy().success(function(user) {
expect(user.deletedAt.getMonth).toBeDefined()
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("keeps the deletedAt-attribute with value null, when running updateAttributes", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
users[0].updateAttributes({username: 'newFnord'}).success(function(user) {
expect(user.deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("keeps the deletedAt-attribute with value null, when updating associations", function(done) {
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
this.ParanoidUser.findAll().success(function(users) {
this.ParanoidUser.create({ username: 'linkedFnord' }).success(function( linkedUser ) {
users[0].setParanoidUser( linkedUser ).success(function(user) {
expect(user.deletedAt).toBe(null)
done()
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
it("can reuse query option objects", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
this.User.findAll(query).success(function(users) {
expect(users[0].username).toEqual('fnord')
this.User.findAll(query).success(function(users) {
expect(users[0].username).toEqual('fnord')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('find', function find() {
it("can reuse query option objects", function(done) {
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
this.User.find(query).success(function(user) {
expect(user.username).toEqual('fnord')
this.User.find(query).success(function(user) {
expect(user.username).toEqual('fnord')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('equals', function find() {
it("can compare records with Date field", function(done) {
this.User.create({ username: 'fnord' }).success(function(user1) {
var query = { where: { username: 'fnord' }}
this.User.find(query).success(function(user2) {
expect(user1.equals(user2)).toBeTrue()
done()
}.bind(this))
}.bind(this))
})
})
describe('updateAttributes', function() {
it('stores and restores null values', function(done) {
var Download = this.sequelize.define('download', {
startedAt: Helpers.Sequelize.DATE,
canceledAt: Helpers.Sequelize.DATE,
finishedAt: Helpers.Sequelize.DATE
})
Download.sync({ force: true }).success(function() {
Download.create({
startedAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt).toBeFalsy()
expect(download.finishedAt).toBeFalsy()
download.updateAttributes({
canceledAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt instanceof Date).toBeTrue()
expect(download.finishedAt).toBeFalsy()
Download.all({
where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL")
}).success(function(downloads) {
downloads.forEach(function(download) {
expect(download.startedAt instanceof Date).toBeTrue()
expect(download.canceledAt instanceof Date).toBeTrue()
expect(download.finishedAt).toBeFalsy()
})
done()
})
})
})
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("Language Util"), function() {
describe("Plural", function(){
before(function(done) {
Helpers.initTests({
dialect: dialect,
onComplete: function(sequelize) {
this.sequelize = sequelize
this.sequelize.options.language = 'es'
done()
}.bind(this)
})
})
it("should rename tables to their plural form...", function(done){
var table = this.sequelize.define('arbol', {name: Sequelize.STRING})
var table2 = this.sequelize.define('androide', {name: Sequelize.STRING})
expect(table.tableName).toEqual('arboles')
expect(table2.tableName).toEqual('androides')
done()
})
it("should be able to pluralize/singularize associations...", function(done){
var table = this.sequelize.define('arbol', {name: Sequelize.STRING})
var table2 = this.sequelize.define('androide', {name: Sequelize.STRING})
var table3 = this.sequelize.define('hombre', {name: Sequelize.STRING})
table.hasOne(table2)
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androides.identifier).toEqual('arbolId')
expect(table2.associations.arboles).toBeDefined()
expect(table3.associations.androideshombres).toBeDefined()
done()
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, Migrator = require("../lib/migrator")
}
buster.spec.expose()
buster.testRunner.timeout = 10000
describe(Helpers.getTestDialectTeaser("Migrator"), function() {
before(function(done) {
this.init = function(options, callback) {
options = Helpers.Sequelize.Utils._.extend({
path: __dirname + '/assets/migrations',
logging: function(){}
}, options || {})
var migrator = new Migrator(this.sequelize, options)
migrator
.findOrCreateSequelizeMetaDAO({ force: true })
.success(function(SequelizeMeta) {
callback && callback(migrator, SequelizeMeta)
})
.error(function(err) { console.log(err) })
}.bind(this)
Helpers.initTests({ dialect: dialect, onComplete: done, context: this })
})
it("as", function() {
expect(1).toEqual(1)
})
describe('getUndoneMigrations', function() {
it("returns no files if timestamps are after the files timestamp", function(done) {
this.init({ from: 20120101010101 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(0)
done()
}.bind(this))
}.bind(this))
})
it("returns only files between from and to", function(done) {
this.init({ from: 19700101000000, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(1)
expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js')
done()
}.bind(this))
}.bind(this))
})
it("returns exactly the migration which is defined in from and to", function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(1)
expect(migrations[migrations.length - 1].filename).toEqual('20111117063700-createPerson.js')
done()
}.bind(this))
}.bind(this))
})
it("returns also the file which is exactly options.from or options.to", function(done) {
this.init({ from: 20111117063700, to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(2)
expect(migrations[0].filename).toEqual('20111117063700-createPerson.js')
expect(migrations[1].filename).toEqual('20111130161100-emptyMigration.js')
done()
}.bind(this))
}.bind(this))
})
it("returns all files to options.to if no options.from is defined", function(done) {
this.init({ to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(2)
done()
}.bind(this))
}.bind(this))
})
it("returns all files from last migration id stored in database", function(done) {
this.init(undefined, function(migrator, SequelizeMeta) {
SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).toBeNull()
expect(migrations.length).toEqual(6)
expect(migrations[0].filename).toEqual('20111130161100-emptyMigration.js')
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('migrations', function() {
before(function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
this.migrator = migrator
this.migrator.migrate().success(done)
}.bind(this))
})
describe('executions', function() {
it("executes migration #20111117063700 and correctly creates the table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ])
done()
})
})
it("executes migration #20111117063700 and correctly adds isBetaMember", function(done) {
this.sequelize.getQueryInterface().describeTable('Person').success(function(data) {
var fields = Helpers.Sequelize.Utils._.keys(data).sort()
expect(fields).toEqual([ 'isBetaMember', 'name' ])
done()
})
})
it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ])
this.migrator.migrate({ method: 'down' }).success(function() {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([])
done()
}.bind(this))
}.bind(this))
}.bind(this))
})
it("executes the empty migration #20111130161100", function(done) {
this.init({ from: 20111130161100, to: 20111130161100 }, function(migrator) {
// this migration isn't actually testing anything but
// should not timeout
expect(1).toEqual(1)
migrator
.migrate()
.success(done)
.error(function(err) { console.log(err) })
})
})
})
describe('renameTable', function() {
before(function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
this.migrator = migrator
this.migrator.migrate().success(done)
}.bind(this))
})
it("executes migration #20111205064000 and renames a table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toContain('Person')
this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'User' ])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('addColumn', function() {
it('adds a column to the user table', function(done) {
this.init({ from: 20111117063700, to: 20111205162700 }, function(migrator) {
migrator.migrate().complete(function(err) {
this.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).toEqual(true)
expect(isAdmin.allowNull).toEqual(false)
expect(isAdmin.defaultValue).toEqual(false)
expect(shopId.allowNull).toEqual(true)
done()
})
}.bind(this))
}.bind(this))
})
})
describe('removeColumn', function() {
it('removes the shopId column from user', function(done) {
this.init({ to: 20111206061400 }, function(migrator) {
migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).toEqual(true)
expect(isAdmin.allowNull).toEqual(false)
expect(isAdmin.defaultValue).toEqual(false)
expect(shopId).toBeFalsy()
done()
})
}.bind(this))
}.bind(this))
})
})
describe('changeColumn', function() {
it('changes the signature column from user to default "signature" + notNull', function(done) {
this.init({ to: 20111206063000 }, function(migrator) {
migrator.migrate().success(function() {
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
if (dialect === 'postgres') {
expect(signature.type).toEqual('CHARACTER VARYING')
} else {
expect(signature.type).toEqual('VARCHAR(255)')
}
expect(signature.allowNull).toEqual(false)
expect(signature.defaultValue).toEqual('Signature')
done()
})
}.bind(this))
}.bind(this))
})
})
})
describe('renameColumn', function() {
it("renames the signature column from user to sig", function(done) {
this.init({ to: 20111206163300 }, function(migrator) {
migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, sig = data.sig
expect(signature).toBeFalsy()
expect(sig).toBeTruthy()
done()
})
}.bind(this))
}.bind(this))
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] hstore', function() {
const hstore = require('../../lib/dialects/postgres/hstore')
describe('stringifyPart', function() {
it("handles undefined values correctly", function() {
expect(hstore.stringifyPart(undefined)).toEqual('NULL')
})
it("handles null values correctly", function() {
expect(hstore.stringifyPart(null)).toEqual('NULL')
})
it("handles boolean values correctly", function() {
expect(hstore.stringifyPart(false)).toEqual('false')
expect(hstore.stringifyPart(true)).toEqual('true')
})
it("handles strings correctly", function() {
expect(hstore.stringifyPart('foo')).toEqual('"foo"')
})
it("handles strings with backslashes correctly", function() {
expect(hstore.stringifyPart("\\'literally\\'")).toEqual('"\\\\\'literally\\\\\'"')
})
it("handles arrays correctly", function() {
expect(hstore.stringifyPart([1,['2'],'"3"'])).toEqual('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
})
it("handles simple objects correctly", function() {
expect(hstore.stringifyPart({ test: 'value' })).toEqual('"{\\"test\\":\\"value\\"}"')
})
it("handles nested objects correctly", function () {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).toEqual('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
})
it("handles objects correctly", function() {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).toEqual('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function() {
expect(hstore.stringify({ })).toEqual('')
})
it('should handle null values correctly', function () {
expect(hstore.stringify({ null: null })).toEqual('"null"=>NULL')
})
it('should handle simple objects correctly', function() {
expect(hstore.stringify({ test: 'value' })).toEqual('"test"=>"value"')
})
it('should handle nested objects correctly', function() {
expect(hstore.stringify({ test: { nested: 'value' } })).toEqual('"test"=>"{\\"nested\\":\\"value\\"}"')
})
it('should handle nested arrays correctly', function() {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).toEqual('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).toEqual('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
})
})
describe('parse', function() {
it('should handle empty objects correctly', function() {
expect(hstore.parse('')).toEqual({ })
})
it('should handle simple objects correctly', function() {
expect(hstore.parse('"test"=>"value"')).toEqual({ test: 'value' })
})
it('should handle nested objects correctly', function() {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).toEqual({ test: { nested: 'value' } })
})
it('should handle nested arrays correctly', function() {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).toEqual({ test: [ 1, '2', [ '"3"' ] ] })
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).toEqual({ true: true, false: false, null: null, undefined: null, integer: 1, array: [1,'2'], object: { object: 'value' }})
})
})
})
}
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Promise"), function () {
before(function (done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function (sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1, 20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
self.ParanoidUser = sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, {
paranoid: true
})
self.ParanoidUser.hasOne(self.ParanoidUser)
},
onComplete: function () {
self.User.sync({ force: true }).then(function () {
return self.HistoryLog.sync({ force: true })
}).then(function () {
return self.ParanoidUser.sync({force: true })
})
.then(function () {done()}, done)
}
})
})
describe('increment', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment(['aNumber'], 2)
}).then(function (user2) {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(2);
done();
}, done);
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
// Select the user again (simulating a concurrent query)
return self.User.find(1).then(function (user2) {
return user2.updateAttributes({
aNumber: user2.aNumber + 1
}).then(function (user3) {
return user1.increment(['aNumber'], 2)
}).then(function (user4) {
return self.User.find(1)
}).then(function (user5) {
expect(user5.aNumber).toBe(3);
done();
}, done)
});
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment({ 'aNumber': 1, 'bNumber': 2})
}).then(function () {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2);
done();
}, done);
});
});
describe('decrement', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('should still work right with other concurrent decrements', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
var _done = _.after(3, function () {
self.User.find(1).then(function (user2) {
expect(user2.aNumber).toEqual(-6);
done();
})
});
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
});
});
});
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function (done) {
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return originalUser.updateAttributes({ username: 'Doe John' }).then(function () {
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser === updatedUser).toBeTrue()
done();
}, done)
})
})
it("should update the values on all references to the DAO", function (done) {
var self = this
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return self.User.find(originalUser.id).then(function (updater) {
return updater.updateAttributes({ username: 'Doe John' })
}).then(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).toEqual('John Doe')
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser.username).toEqual('Doe John')
expect(updatedUser.username).toEqual('Doe John')
done();
}, done)
})
})
it("should update the associations as well", function (done) {
var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING })
, Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
this.sequelize.sync({ force: true }).then(function () {
return Book.create({ title: 'A very old book' })
}).then(function (book) {
return Page.create({ content: 'om nom nom' }).then(function (page) {
return book.setPages([ page ]).then(function () {
return Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).then(function (leBook) {
return page.updateAttributes({ content: 'something totally different' }).then(function (page) {
expect(leBook.pages[0].content).toEqual('om nom nom')
expect(page.content).toEqual('something totally different')
return leBook.reload().then(function (leBook) {
expect(leBook.pages[0].content).toEqual('something totally different')
expect(page.content).toEqual('something totally different')
done()
})
})
})
})
})
}, done)
})
});
describe('complete', function () {
it("gets triggered if an error occurs", function (done) {
this.User.find({ where: "asdasdasd" }).then(null, function (err) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function (done) {
this.User.count().then(function (result) {
expect(result).toBeDefined()
done()
})
})
})
describe('save', function () {
it('should fail a validation upon creating', function (done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1);
done();
});
})
it('should fail a validation upon building', function (done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateCustom).toBeDefined()
expect(err.validateCustom).toBeArray()
expect(err.validateCustom[0]).toBeDefined()
expect(err.validateCustom[0]).toEqual('Length failed.')
done()
})
})
it('should fail a validation when updating', function (done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeDefined()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1)
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("QueryInterface"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: function() {
this.interface = this.sequelize.getQueryInterface()
done()
}.bind(this)
})
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
this.interface.createTable('table', { name: Helpers.Sequelize.STRING }).complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(1)
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('indexes', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN
}).success(done)
})
it('adds, reads and removes an index to the table', function(done) {
this.interface.addIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
var indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual(['user_username_is_admin'])
this.interface.removeIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual([])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('describeTable', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN,
enumVals: Helpers.Sequelize.ENUM('hello', 'world')
}).success(done)
})
it('reads the metadata of the table', function(done) {
this.interface.describeTable('User').complete(function(err, metadata) {
expect(err).toBeNull()
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).toEqual(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).toBeTrue()
expect(username.defaultValue).toBeNull()
expect(isAdmin.type).toEqual(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).toBeTrue()
expect(isAdmin.defaultValue).toBeNull()
if (dialect === 'postgres') {
expect(enumVals.special).toBeArray();
expect(enumVals.special.length).toEqual(2);
}
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') {
return '"' + str + '"'
} else if (dialect == 'mysql') {
return '`' + str + '`'
} else {
return str
}
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Sequelize"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) { this.sequelize = sequelize }.bind(this),
onComplete: done
})
})
describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).toBeFalse()
})
it("returns true if the dao was defined before", function() {
this.sequelize.define('Project', {
name: Helpers.Sequelize.STRING
})
expect(this.sequelize.isDefined('Project')).toBeTrue()
})
})
describe('query', function() {
before(function(done) {
this.User = this.sequelize.define('User', {
username: Helpers.Sequelize.STRING
})
this.insertQuery = "INSERT INTO " + qq(this.User.tableName) + " (username, " + qq("createdAt") + ", " + qq("updatedAt") + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"
this.User.sync().success(done).error(function(err) {
console(err)
done()
})
})
it('executes a query the internal way', function(done) {
this.sequelize.query(this.insertQuery, null, { raw: true })
.complete(function(err, result) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(result).toBeNull()
done()
})
})
it('executes a query if only the sql is passed', function(done) {
this.sequelize.query(this.insertQuery)
.complete(function(err, result) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(result).not.toBeDefined()
done()
})
})
it('executes select queries correctly', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select * from " + qq(this.User.tableName) + "")
.complete(function(err, users) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
})
it('executes select query and parses dot notation results', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize
.query("select username as " + qq("user.username") + " from " + qq(this.User.tableName) + "")
.complete(function(err, users) {
if (err) {
console.log(err)
}
expect(err).toBeNull()
expect(users.map(function(u){ return u.user })).toEqual([{'username':'john'}])
done()
})
}.bind(this))
})
if (dialect == 'mysql') {
it('executes stored procedures', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() {
this.sequelize.query(
"CREATE PROCEDURE foo()\nSELECT * FROM " + this.User.tableName + ";"
).success(function() {
this.sequelize.query('CALL foo()').success(function(users) {
expect(users.map(function(u){ return u.username })).toEqual(['john'])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
} else {
console.log('FIXME: I want to be supported in this dialect as well :-(')
}
it('uses the passed DAOFactory', function(done) {
this.sequelize.query(this.insertQuery).success(function() {
this.sequelize.query("SELECT * FROM " + qq(this.User.tableName) + ";", this.User).success(function(users) {
expect(users[0].__factory).toEqual(this.User)
done()
}.bind(this))
}.bind(this))
})
it('destructs dot separated attributes when doing a raw query', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`'
, sql = "select 1 as " + Helpers.Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true }).success(function(result) {
expect(result).toEqual([ { foo: { bar: { baz: 1 } } } ])
done()
})
})
it('replaces token with the passed array', function(done) {
this.sequelize.query('select ? as foo, ? as bar', null, { raw: true }, [ 1, 2 ]).success(function(result) {
expect(result).toEqual([{ foo: 1, bar: 2 }])
done()
})
})
})
describe('define', function() {
[
{ type: Helpers.Sequelize.ENUM, values: ['scheduled', 'active', 'finished']},
Helpers.Sequelize.ENUM('scheduled', 'active', 'finished')
].forEach(function(status) {
describe('enum', function() {
before(function(done) {
this.Review = this.sequelize.define('review', { status: status })
this.Review.sync({ force: true }).success(done)
})
it('raises an error if no values are defined', function() {
Helpers.assertException(function() {
this.sequelize.define('omnomnom', {
bla: { type: Helpers.Sequelize.ENUM }
})
}.bind(this), 'Values for ENUM haven\'t been defined.')
})
it('correctly stores values', function(done) {
this.Review.create({ status: 'active' }).success(function(review) {
expect(review.status).toEqual('active')
done()
})
})
it('correctly loads values', function(done) {
this.Review.create({ status: 'active' }).success(function() {
this.Review.findAll().success(function(reviews) {
expect(reviews[0].status).toEqual('active')
done()
})
}.bind(this))
})
it("doesn't save an instance if value is not in the range of enums", function() {
Helpers.assertException(function() {
this.Review.create({ status: 'fnord' })
}.bind(this), 'Value "fnord" for ENUM status is out of allowed scope. Allowed values: scheduled, active, finished')
})
})
})
describe('table', function() {
[
{ id: { type: Helpers.Sequelize.BIGINT } },
{ id: { type: Helpers.Sequelize.STRING, allowNull: true } },
{ id: { type: Helpers.Sequelize.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].forEach(function(customAttributes) {
it('should be able to override options on the default attributes', function(done) {
var Picture = this.sequelize.define('picture', Helpers.Sequelize.Utils._.cloneDeep(customAttributes))
Picture.sync({ force: true }).success(function() {
Object.keys(customAttributes).forEach(function(attribute) {
Object.keys(customAttributes[attribute]).forEach(function(option) {
var optionValue = customAttributes[attribute][option];
expect(Picture.rawAttributes[attribute][option]).toBe(optionValue)
});
})
done()
})
})
})
})
})
})
......@@ -6,7 +6,7 @@ module.exports = {
pool: { maxConnections: 5, maxIdleTime: 30000},
rand: function() {
return parseInt(Math.random() * 999)
return parseInt(Math.random() * 999, 10)
},
//make maxIdleTime small so that tests exit promptly
......@@ -26,6 +26,6 @@ module.exports = {
database: 'sequelize_test',
username: "postgres",
port: 5432,
pool: { maxConnections: 5, maxIdleTime: 30}
pool: { maxConnections: 5, maxIdleTime: 3000}
}
}
var chai = require('chai')
, expect = chai.expect
, config = require(__dirname + "/config/config")
, Support = require(__dirname + '/support')
, dialect = Support.getTestDialect()
, Sequelize = require(__dirname + '/../index')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it("when we don't have the correct server details", function(done) {
// mysql is not properly supported due to the custom pooling system
if (dialect !== "postgres" && dialect !== "postgres-native") {
console.log('This dialect doesn\'t support me :(')
expect(true).to.be.true // Silence Buster
return done()
}
var seq = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
seq.query('select 1 as hello').error(function(err) {
expect(err.message).to.match(/Failed to find (.*?) Please double check your settings\./)
done()
})
})
it('when we don\'t have the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native") {
console.log('This dialect doesn\'t support me :(')
expect(true).to.be.true // Silence Buster
return done()
}
var seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
seq.query('select 1 as hello').error(function(err) {
expect(err.message).to.match(/^Failed to authenticate/)
done()
})
})
it('when we don\'t have a valid dialect.', function(done) {
expect(function() {
new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {host: '0.0.0.1', port: config[dialect].port, dialect: undefined})
}).to.throw(Error, 'The dialect undefined is not supported.')
done()
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
var config = sequelize.config
var options = sequelize.options
expect(options.dialect).to.equal('mysql')
expect(config.database).to.equal('dbname')
expect(config.host).to.equal('example.com')
expect(config.username).to.equal('user')
expect(config.password).to.equal('pass')
expect(config.port).to.equal('9821')
})
it('should work with no authentication options', function(done) {
var sequelize = new Sequelize('mysql://example.com:9821/dbname')
var config = sequelize.config
expect(config.username).to.not.be.ok
expect(config.password).to.be.null
done()
})
it('should use the default port when no other is specified', function(done) {
var sequelize = new Sequelize('mysql://example.com/dbname')
var config = sequelize.config
// The default port should be set
expect(config.port).to.equal(3306)
done()
})
})
describe('Intantiation with arguments', function() {
it('should accept two parameters (database, username)', function(done) {
var sequelize = new Sequelize('dbname', 'root')
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
done()
})
it('should accept three parameters (database, username, password)', function(done) {
var sequelize = new Sequelize('dbname', 'root', 'pass')
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
expect(config.password).to.equal('pass')
done()
})
it('should accept four parameters (database, username, password, options)', function(done) {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var config = sequelize.config
expect(config.database).to.equal('dbname')
expect(config.username).to.equal('root')
expect(config.password).to.equal('pass')
expect(config.port).to.equal(999)
done()
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, Sequelize = require('../index')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAOFactory"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe('constructor', function() {
it("uses the passed dao name as tablename if freezeTableName", function(done) {
var User = this.sequelize.define('FrozenUser', {}, { freezeTableName: true })
expect(User.tableName).to.equal('FrozenUser')
done()
})
it("uses the pluralized dao name as tablename unless freezeTableName", function(done) {
var User = this.sequelize.define('SuperUser', {}, { freezeTableName: false })
expect(User.tableName).to.equal('SuperUsers')
done()
})
it("uses checks to make sure dao factory isnt leaking on multiple define", function(done) {
this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize = this.sequelize.daoFactoryManager.all.length
this.sequelize.define('SuperUser', {}, { freezeTableName: false })
var factorySize2 = this.sequelize.daoFactoryManager.all.length
expect(factorySize).to.equal(factorySize2)
done()
})
it("attaches class and instance methods", function(done) {
var User = this.sequelize.define('UserWithClassAndInstanceMethods', {}, {
classMethods: { doSmth: function(){ return 1 } },
instanceMethods: { makeItSo: function(){ return 2}}
})
expect(User.doSmth).to.exist
expect(User.doSmth()).to.equal(1)
expect(User.makeItSo).not.to.exist
expect(User.build().doSmth).not.to.exist
expect(User.build().makeItSo).to.exist
expect(User.build().makeItSo()).to.equal(2)
done()
})
it("throws an error if 2 autoIncrements are passed", function(done) {
var self = this
expect(function() {
self.sequelize.define('UserWithTwoAutoIncrements', {
userid: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
userscore: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }
})
}).to.throw(Error, 'Invalid DAO definition. Only one autoincrement field allowed.')
done()
})
it('throws an error if a custom model-wide validation is not a function', function(done) {
var self = this
expect(function() {
self.sequelize.define('Foo', {
field: Sequelize.INTEGER
}, {
validate: {
notFunction: 33
}
})
}).to.throw(Error, 'Members of the validate option must be functions. Model: Foo, error with validate member notFunction')
done()
})
it('throws an error if a custom model-wide validation has the same name as a field', function(done) {
var self = this
expect(function() {
self.sequelize.define('Foo', {
field: Sequelize.INTEGER
}, {
validate: {
field: function() {}
}
})
}).to.throw(Error, 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field')
done()
})
})
describe('build', function() {
it("doesn't create database entries", function(done) {
this.User.build({ username: 'John Wayne' })
this.User.all().success(function(users) {
expect(users).to.have.length(0)
done()
})
})
it("fills the objects with default values", function(done) {
var Task = this.sequelize.define('TaskBuild', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
})
expect(Task.build().title).to.equal('a task!')
expect(Task.build().foo).to.equal(2)
expect(Task.build().bar).to.not.be.ok
expect(Task.build().foobar).to.equal('asd')
expect(Task.build().flag).to.be.false
done()
})
it("fills the objects with default values", function(done) {
var Task = this.sequelize.define('TaskBuild', {
title: {type: Sequelize.STRING, defaultValue: 'a task!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
bar: {type: Sequelize.DATE},
foobar: {type: Sequelize.TEXT, defaultValue: 'asd'},
flag: {type: Sequelize.BOOLEAN, defaultValue: false}
}, { timestamps: false })
expect(Task.build().title).to.equal('a task!')
expect(Task.build().foo).to.equal(2)
expect(Task.build().bar).to.not.be.ok
expect(Task.build().foobar).to.equal('asd')
expect(Task.build().flag).to.be.false
done()
})
it("stores the the passed values in a special variable", function(done) {
var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).to.deep.equal({ username: 'John Wayne' })
done()
})
it("attaches getter and setter methods from attribute definition", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters1', {
price: {
type: Sequelize.INTEGER,
get : function() {
return 'answer = ' + this.getDataValue('price')
},
set : function(v) {
return this.setDataValue('price', v + 42)
}
}
})
expect(Product.build({price: 42}).price).to.equal('answer = 84')
var p = Product.build({price: 1})
expect(p.price).to.equal('answer = 43');
p.price = 0
expect(p.price).to.equal('answer = 42')
done()
})
it("attaches getter and setter methods from options", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters2', {
priceInCents: Sequelize.INTEGER
},{
setterMethods: {
price: function(value) {
this.dataValues.priceInCents = value * 100
}
},
getterMethods: {
price: function() {
return '$' + (this.getDataValue('priceInCents') / 100)
},
priceInCents: function() {
return this.dataValues.priceInCents
}
}
});
expect(Product.build({price: 20}).priceInCents).to.equal(20 * 100)
expect(Product.build({priceInCents: 30 * 100}).price).to.equal('$' + 30)
done()
})
it("attaches getter and setter methods from options only if not defined in attribute", function(done) {
var Product = this.sequelize.define('ProductWithSettersAndGetters3', {
price1: {
type: Sequelize.INTEGER,
set : function(v) { this.setDataValue('price1', v * 10) }
},
price2: {
type: Sequelize.INTEGER,
get : function() { return this.getDataValue('price2') * 10 }
}
},{
setterMethods: {
price1: function(v) { this.setDataValue('price1', v * 100) }
},
getterMethods: {
price2: function() { return '$' + this.getDataValue('price2') }
}
});
var p = Product.build({ price1: 1, price2: 2 })
expect(p.price1).to.equal(10)
expect(p.price2).to.equal(20)
done()
})
})
describe('findOrCreate', function () {
it("Returns instace if already existent. Single find field.", function(done) {
var self = this,
data = {
username: 'Username'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate({
username: user.username
}).success(function (_user, created) {
expect(_user.id).to.equal(user.id)
expect(_user.username).to.equal('Username')
expect(created).to.be.false
done()
})
})
})
it("Returns instace if already existent. Multiple find fields.", function(done) {
var self = this,
data = {
username: 'Username',
data: 'ThisIsData'
};
this.User.create(data).success(function (user) {
self.User.findOrCreate(data).success(function (_user, created) {
expect(_user.id).to.equal(user.id)
expect(_user.username).to.equal('Username')
expect(_user.data).to.equal('ThisIsData')
expect(created).to.be.false
done()
})
})
})
it("creates new instance with default value.", function(done) {
var data = {
username: 'Username'
},
default_values = {
data: 'ThisIsData'
};
this.User.findOrCreate(data, default_values).success(function(user, created) {
expect(user.username).to.equal('Username')
expect(user.data).to.equal('ThisIsData')
expect(created).to.be.true
done()
})
})
})
describe('create', function() {
it("casts empty arrays correctly for postgresql", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').to.equal('')
return done()
}
var User = this.sequelize.define('UserWithArray', {
myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
})
User.sync({force: true}).success(function() {
User.create({myvals: [], mystr: []}).on('sql', function(sql){
expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1)
expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1)
done()
})
})
})
it("doesn't allow duplicated records with unique:true", function(done) {
var User = this.sequelize.define('UserWithUniqueUsername', {
username: { type: Sequelize.STRING, unique: true }
})
User.sync({ force: true }).success(function() {
User.create({ username:'foo' }).success(function() {
User.create({ username: 'foo' }).error(function(err) {
expect(err).to.exist
if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
}
else if (dialect === "mysql") {
expect(err.message).to.match(/.*Duplicate\ entry.*/)
} else {
expect(err.message).to.match(/.*duplicate\ key\ value.*/)
}
done()
})
})
})
})
it("raises an error if created object breaks definition contraints", function(done) {
var UserNull = this.sequelize.define('UserWithNonNullSmth', {
username: { type: Sequelize.STRING, unique: true },
smth: { type: Sequelize.STRING, allowNull: false }
})
UserNull.sync({ force: true }).success(function() {
UserNull.create({ username: 'foo', smth: null }).error(function(err) {
expect(err).to.exist
if (dialect === "mysql") {
// We need to allow two different errors for MySQL, see:
// http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_trans_tables
expect(err.message).to.match(/(Column 'smth' cannot be null|Field 'smth' doesn't have a default value)/)
}
else if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
} else {
expect(err.message).to.match(/.*column "smth" violates not-null.*/)
}
UserNull.create({ username: 'foo', smth: 'foo' }).success(function() {
UserNull.create({ username: 'foo', smth: 'bar' }).error(function(err) {
expect(err).to.exist
if (dialect === "sqlite") {
expect(err.message).to.match(/.*SQLITE_CONSTRAINT.*/)
}
else if (dialect === "mysql") {
expect(err.message).to.match(/Duplicate entry 'foo' for key 'username'/)
} else {
expect(err.message).to.match(/.*duplicate key value violates unique constraint.*/)
}
done()
})
})
})
})
})
it('raises an error if you mess up the datatype', function(done) {
var self = this
expect(function() {
self.sequelize.define('UserBadDataType', {
activity_date: Sequelize.DATe
})
}).to.throw(Error, 'Unrecognized data type for field activity_date')
expect(function() {
self.sequelize.define('UserBadDataType', {
activity_date: {type: Sequelize.DATe}
})
}).to.throw(Error, 'Unrecognized data type for field activity_date')
done()
})
it('sets a 64 bit int in bigint', function(done) {
var User = this.sequelize.define('UserWithBigIntFields', {
big: Sequelize.BIGINT
})
User.sync({ force: true }).success(function() {
User.create({ big: '9223372036854775807' }).on('success', function(user) {
expect(user.big).to.be.equal( '9223372036854775807' )
done()
})
})
})
it('sets auto increment fields', function(done) {
var User = this.sequelize.define('UserWithAutoIncrementField', {
userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false }
})
User.sync({ force: true }).success(function() {
User.create({}).on('success', function(user) {
expect(user.userid).to.equal(1)
User.create({}).on('success', function(user) {
expect(user.userid).to.equal(2)
done()
})
})
})
})
it('allows the usage of options as attribute', function(done) {
var User = this.sequelize.define('UserWithNameAndOptions', {
name: Sequelize.STRING,
options: Sequelize.TEXT
})
var options = JSON.stringify({ foo: 'bar', bar: 'foo' })
User.sync({ force: true }).success(function() {
User
.create({ name: 'John Doe', options: options })
.success(function(user) {
expect(user.options).to.equal(options)
done()
})
})
})
it('allows sql logging', function(done) {
var User = this.sequelize.define('UserWithUniqueNameAndNonNullSmth', {
name: {type: Sequelize.STRING, unique: true},
smth: {type: Sequelize.STRING, allowNull: false}
})
User.sync({ force: true }).success(function() {
User
.create({ name: 'Fluffy Bunny', smth: 'else' })
.on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("INSERT")).to.be.above(-1)
done()
})
})
})
it('should only store the values passed in the whitelist', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data, ['username']).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).to.equal(data.username)
expect(_user.secretValue).not.to.equal(data.secretValue)
expect(_user.secretValue).to.equal(null)
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = { username: 'Peter', secretValue: '42' }
this.User.create(data).success(function(user) {
self.User.find(user.id).success(function(_user) {
expect(_user.username).to.equal(data.username)
expect(_user.secretValue).to.equal(data.secretValue)
done()
})
})
})
it('can omitt autoincremental columns', function(done) {
var self = this
, data = { title: 'Iliad' }
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
, chain = new Sequelize.Utils.QueryChainer()
, chain2 = new Sequelize.Utils.QueryChainer()
, books = []
dataTypes.forEach(function(dataType, index) {
books[index] = self.sequelize.define('Book'+index, {
id: { type: dataType, primaryKey: true, autoIncrement: true },
title: Sequelize.TEXT
})
})
books.forEach(function(b) {
chain.add(b.sync({ force: true }))
})
chain.run().success(function() {
books.forEach(function(b) {
chain2.add(b.create(data))
})
chain2.run().success(function(results) {
results.forEach(function(book, index) {
expect(book.title).to.equal(data.title)
expect(book.author).to.equal(data.author)
expect(books[index].rawAttributes.id.type.toString())
.to.equal(dataTypes[index].toString())
})
done()
})
})
})
it('saves data with single quote', function(done) {
var quote = "single'quote"
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).to.equal(quote)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var quote = 'double"quote'
, self = this
this.User.create({ data: quote }).success(function(user) {
expect(user.data).to.equal(quote)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var json = JSON.stringify({ key: 'value' })
, self = this
this.User.create({ data: json }).success(function(user) {
expect(user.data).to.equal(json)
self.User.find({where: { id: user.id }}).success(function(user) {
expect(user.data).to.equal(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
this.User.create({ username: 'foo' }).success(function(user) {
expect(parseInt(+user.createdAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1.5)
done()
})
})
it('allows setting custom IDs', function(done) {
var self = this
this.User.create({ id: 42 }).success(function (user) {
expect(user.id).to.equal(42)
self.User.find(42).success(function (user) {
expect(user).to.exist
done()
})
})
})
describe('enums', function() {
it('correctly restores enum values', function(done) {
var self = this
, Item = self.sequelize.define('Item', {
state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }
})
Item.sync({ force: true }).success(function() {
Item.create({ state: 'available' }).success(function(_item) {
Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.id).to.equal(_item.id)
done()
})
})
})
})
})
})
describe('bulkCreate', function() {
it('properly handles disparate field lists', function(done) {
var self = this
, data = [{username: 'Peter', secretValue: '42' },
{username: 'Paul'},
{username: 'Steve'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({where: {username: 'Paul'}}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("Paul")
expect(users[0].secretValue).to.be.null
done()
})
})
})
it('inserts multiple values respecting the white list', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data, ['username']).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].secretValue).to.be.null;
expect(users[1].username).to.equal("Paul")
expect(users[1].secretValue).to.be.null;
done()
})
})
})
it('should store all values if no whitelist is specified', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '23'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].secretValue).to.equal('42')
expect(users[1].username).to.equal("Paul")
expect(users[1].secretValue).to.equal('23')
done()
})
})
})
it('saves data with single quote', function(done) {
var self = this
, quote = "Single'Quote"
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(quote)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(quote)
done()
})
})
})
it('saves data with double quote', function(done) {
var self = this
, quote = 'Double"Quote'
, data = [{ username: 'Peter', data: quote},
{ username: 'Paul', data: quote}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(quote)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(quote)
done()
})
})
})
it('saves stringified JSON data', function(done) {
var self = this
, json = JSON.stringify({ key: 'value' })
, data = [{ username: 'Peter', data: json},
{ username: 'Paul', data: json}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(users[0].data).to.equal(json)
expect(users[1].username).to.equal("Paul")
expect(users[1].data).to.equal(json)
done()
})
})
})
it('stores the current date in createdAt', function(done) {
var self = this
, data = [{ username: 'Peter'},
{ username: 'Paul'}]
this.User.bulkCreate(data).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].username).to.equal("Peter")
expect(parseInt(+users[0].createdAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
expect(users[1].username).to.equal("Paul")
expect(parseInt(+users[1].createdAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
done()
})
})
})
describe('enums', function() {
it('correctly restores enum values', function(done) {
var self = this
, Item = self.sequelize.define('Item', {
state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] },
name: Sequelize.STRING
})
Item.sync({ force: true }).success(function() {
Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() {
Item.find({ where: { state: 'available' }}).success(function(item) {
expect(item.name).to.equal('B')
done()
})
})
})
})
})
})
describe('update', function() {
it('allows sql logging of updated statements', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
}, {
paranoid:true
})
User.sync({ force: true }).success(function() {
User.create({ name: 'meg', bio: 'none' }).success(function(u) {
expect(u).to.exist
expect(u).not.to.be.null
u.updateAttributes({name: 'brian'}).on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("UPDATE")).to.be.above(-1)
done()
})
})
})
})
it('updates only values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
users.forEach(function (user) {
if (user.secretValue == '42') {
expect(user.username).to.equal("Bill")
} else {
expect(user.username).to.equal("Bob")
}
})
done()
})
})
})
})
it('sets updatedAt to the current timestamp', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.update({username: 'Bill'}, {secretValue: '42'}).success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
expect(users[0].username).to.equal("Bill")
expect(users[1].username).to.equal("Bill")
expect(users[2].username).to.equal("Bob")
expect(parseInt(+users[0].updatedAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
expect(parseInt(+users[1].updatedAt/5000, 10)).to.equal(parseInt(+new Date()/5000, 10))
done()
})
})
})
})
})
describe('destroy', function() {
it('deletes a record from the database if dao is not paranoid', function(done) {
var UserDestroy = this.sequelize.define('UserDestory', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
UserDestroy.sync({ force: true }).success(function() {
UserDestroy.create({name: 'hallo', bio: 'welt'}).success(function(u) {
UserDestroy.all().success(function(users) {
expect(users.length).to.equal(1)
u.destroy().success(function() {
UserDestroy.all().success(function(users) {
expect(users.length).to.equal(0)
done()
})
})
})
})
})
})
it('allows sql logging of delete statements', function(done) {
var UserDelete = this.sequelize.define('UserDelete', {
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
UserDelete.sync({ force: true }).success(function() {
UserDelete.create({name: 'hallo', bio: 'welt'}).success(function(u) {
UserDelete.all().success(function(users) {
expect(users.length).to.equal(1)
u.destroy().on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("DELETE")).to.be.above(-1)
done()
})
})
})
})
})
it('deletes values that match filter', function(done) {
var self = this
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
this.User.bulkCreate(data).success(function() {
self.User.destroy({secretValue: '42'})
.success(function() {
self.User.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("Bob")
done()
})
})
})
})
it('sets deletedAt to the current timestamp if paranoid is true', function(done) {
var self = this
, ParanoidUser = self.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, {
paranoid: true
})
, data = [{ username: 'Peter', secretValue: '42' },
{ username: 'Paul', secretValue: '42' },
{ username: 'Bob', secretValue: '43' }]
ParanoidUser.sync({ force: true }).success(function() {
ParanoidUser.bulkCreate(data).success(function() {
var date = parseInt(+new Date()/5000, 10)
ParanoidUser.destroy({secretValue: '42'}).success(function() {
ParanoidUser.findAll({order: 'id'}).success(function(users) {
expect(users.length).to.equal(3)
expect(users[0].username).to.equal("Peter")
expect(users[1].username).to.equal("Paul")
expect(users[2].username).to.equal("Bob")
expect(parseInt(+users[0].deletedAt/5000, 10)).to.equal(date)
expect(parseInt(+users[1].deletedAt/5000, 10)).to.equal(date)
done()
})
})
})
})
})
})
describe('special where conditions/smartWhere object', function() {
beforeEach(function(done) {
var self = this
this.User.bulkCreate([
{username: 'boo', intVal: 5, theDate: '2013-01-01 12:00'},
{username: 'boo2', intVal: 10, theDate: '2013-01-10 12:00'}
]).success(function(user2){
done()
})
})
it('should be able to retun a record with primaryKey being null for new inserts', function(done) {
var Session = this.sequelize.define('Session', {
token: { type: DataTypes.TEXT, allowNull: false },
lastUpdate: { type: DataTypes.DATE, allowNull: false }
}, {
charset: 'utf8',
collate: 'utf8_general_ci',
omitNull: true
})
, User = this.sequelize.define('User', {
name: { type: DataTypes.STRING, allowNull: false, unique: true },
password: { type: DataTypes.STRING, allowNull: false },
isAdmin: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
}, {
charset: 'utf8',
collate: 'utf8_general_ci'
})
User.hasMany(Session, { as: 'Sessions' })
Session.belongsTo(User)
Session.sync({ force: true }).success(function() {
User.sync({ force: true }).success(function() {
User.create({name: 'Name1', password: '123', isAdmin: false}).success(function(user) {
var sess = Session.build({
lastUpdate: new Date(),
token: '123'
})
user.addSession(sess).success(function(u) {
expect(u.token).to.equal('123')
done()
})
})
})
})
})
it('should be able to find a row between a certain date', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row between a certain date and an additional where clause', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
},
intVal: 10
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row not between a certain integer', function(done) {
this.User.findAll({
where: {
intVal: {
nbetween: [8, 10]
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not between and between logic', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2012-12-10', '2013-01-02'],
nbetween: ['2013-01-04', '2013-01-20']
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not between and between logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
between: [new Date('2012-12-10'), new Date('2013-01-02')],
nbetween: [new Date('2013-01-04'), new Date('2013-01-20')]
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using greater than or equal to logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
gte: new Date('2013-01-09')
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo2')
expect(users[0].intVal).to.equal(10)
done()
})
})
it('should be able to find a row using greater than or equal to', function(done) {
this.User.find({
where: {
intVal: {
gte: 6
}
}
}).success(function(user) {
expect(user.username).to.equal('boo2')
expect(user.intVal).to.equal(10)
done()
})
})
it('should be able to find a row using greater than', function(done) {
this.User.find({
where: {
intVal: {
gt: 5
}
}
}).success(function(user) {
expect(user.username).to.equal('boo2')
expect(user.intVal).to.equal(10)
done()
})
})
it('should be able to find a row using lesser than or equal to', function(done) {
this.User.find({
where: {
intVal: {
lte: 5
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should be able to find a row using lesser than', function(done) {
this.User.find({
where: {
intVal: {
lt: 6
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should have no problem finding a row using lesser and greater than', function(done) {
this.User.findAll({
where: {
intVal: {
lt: 6,
gt: 4
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
done()
})
})
it('should be able to find a row using not equal to logic', function(done) {
this.User.find({
where: {
intVal: {
ne: 10
}
}
}).success(function(user) {
expect(user.username).to.equal('boo')
expect(user.intVal).to.equal(5)
done()
})
})
it('should be able to find multiple users with any of the special where logic properties', function(done) {
this.User.findAll({
where: {
intVal: {
lte: 10
}
}
}).success(function(users) {
expect(users[0].username).to.equal('boo')
expect(users[0].intVal).to.equal(5)
expect(users[1].username).to.equal('boo2')
expect(users[1].intVal).to.equal(10)
done()
})
})
})
describe('find', function() {
describe('general / basic function', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.UserPrimary = self.sequelize.define('UserPrimary', {
specialKey: {
type: DataTypes.STRING,
primaryKey: true
}
})
self.UserPrimary.sync({force: true}).success(function() {
self.UserPrimary.create({specialKey: 'a string'}).success(function() {
self.user = user
done()
})
})
})
})
it('doesn\'t throw an error when entering in a non integer value for a specified primary field', function(done) {
this.UserPrimary.find('a string').success(function(user) {
expect(user.specialKey).to.equal('a string')
done()
})
})
it('doesn\'t throw an error when entering in a non integer value', function(done) {
this.User.find('a string value').success(function(user) {
expect(user).to.be.null
done()
})
})
it('returns a single dao', function(done) {
var self = this
this.User.find(this.user.id).success(function(user) {
expect(Array.isArray(user)).to.not.be.ok
expect(user.id).to.equal(self.user.id)
expect(user.id).to.equal(1)
done()
})
})
it('returns a single dao given a string id', function(done) {
var self = this
this.User.find(this.user.id + '').success(function(user) {
expect(Array.isArray(user)).to.not.be.ok
expect(user.id).to.equal(self.user.id)
expect(user.id).to.equal(1)
done()
})
})
it("should make aliased attributes available", function(done) {
this.User.find({
where: { id: 1 },
attributes: ['id', ['username', 'name']]
}).success(function(user) {
expect(user.name).to.equal('barfooz')
done()
})
})
it("should not try to convert boolean values if they are not selected", function(done) {
var UserWithBoolean = this.sequelize.define('UserBoolean', {
active: Sequelize.BOOLEAN
})
UserWithBoolean.sync({force: true}).success(function () {
UserWithBoolean.create({ active: true }).success(function(user) {
UserWithBoolean.find({ where: { id: user.id }, attributes: [ 'id' ] }).success(function(user) {
expect(user.active).not.to.exist
done()
})
})
})
})
it('finds a specific user via where option', function(done) {
this.User.find({ where: { username: 'barfooz' } }).success(function(user) {
expect(user.username).to.equal('barfooz')
done()
})
})
it("doesn't find a user if conditions are not matching", function(done) {
this.User.find({ where: { username: 'foo' } }).success(function(user) {
expect(user).to.be.null
done()
})
})
it('allows sql logging', function(done) {
this.User.find({ where: { username: 'foo' } }).on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it('ignores passed limit option', function(done) {
this.User.find({ limit: 10 }).success(function(user) {
// it returns an object instead of an array
expect(Array.isArray(user)).to.not.be.ok
expect(user.hasOwnProperty('username')).to.be.ok
done()
})
})
it('finds entries via primary keys', function(done) {
var self = this
, UserPrimary = self.sequelize.define('UserWithPrimaryKey', {
identifier: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING
})
UserPrimary.sync({ force: true }).success(function() {
UserPrimary.create({
identifier: 'an identifier',
name: 'John'
}).success(function(u) {
expect(u.id).not.to.exist
UserPrimary.find('an identifier').success(function(u2) {
expect(u2.identifier).to.equal('an identifier')
expect(u2.name).to.equal('John')
done()
})
})
})
})
it('returns the selected fields as instance.selectedValues', function(done) {
var self = this
this.User.create({
username: 'JohnXOXOXO'
}).success(function() {
self.User.find({
where: { username: 'JohnXOXOXO' },
attributes: ['username']
}).success(function(user) {
expect(user.selectedValues).to.have.property('username', 'JohnXOXOXO')
done()
})
})
})
it('returns the selected fields and all fields of the included table as instance.selectedValues', function(done) {
var self = this
self.Mission = self.sequelize.define('Mission', {
title: {type: Sequelize.STRING, defaultValue: 'a mission!!'},
foo: {type: Sequelize.INTEGER, defaultValue: 2},
})
self.Mission.belongsTo(self.User)
self.User.hasMany(self.Mission)
self.Mission.sync({ force: true }).success(function() {
self.Mission.create().success(function(mission) {
self.User.create({username: 'John DOE'}).success(function(user) {
mission.setUser(user).success(function() {
self.User.find({
where: { username: 'John DOE' },
attributes: ['username'],
include: [self.Mission]
}).success(function(user) {
expect(user.selectedValues).to.deep.equal({ username: 'John DOE' })
done()
})
})
})
})
})
})
it('always honors ZERO as primary key', function(_done) {
var self = this
, permutations = [
0,
'0',
{where: {id: 0}},
{where: {id: '0'}}
]
, done = _.after(2 * permutations.length, _done);
this.User.bulkCreate([{username: 'jack'}, {username: 'jack'}]).success(function() {
permutations.forEach(function(perm) {
self.User.find(perm).done(function(err, user) {
expect(err).to.be.null;
expect(user).to.be.null;
done();
}).on('sql', function(s) {
expect(s.indexOf(0)).not.to.equal(-1);
done();
})
})
})
})
})
describe('eager loading', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
this.init = function(callback) {
self.Task.sync({ force: true }).success(function() {
self.Worker.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
callback()
})
})
})
})
}
done()
})
describe('belongsTo', function() {
describe('generic', function() {
it('throws an error about unexpected input if include contains a non-object', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ 1 ] })
}).to.throw(Error, 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
done()
})
it('throws an error about missing attributes if include contains an object with daoFactory', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Worker } ] })
}).to.throw(Error, 'Include malformed. Expected attributes: daoFactory, as!')
done()
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('returns the associated worker via task.worker', function(done) {
var self = this
this.Task.belongsTo(this.Worker)
this.init(function() {
self.task.setWorker(self.worker).success(function() {
self.Task.find({
where: { title: 'homework' },
include: [ self.Worker ]
}).complete(function(err, task) {
expect(err).to.be.null
expect(task).to.exist
expect(task.worker).to.exist
expect(task.worker.name).to.equal('worker')
done()
})
})
})
})
})
it('returns the private and public ip', function(done) {
var self = Object.create(this)
self.Domain = self.sequelize.define('Domain', { ip: Sequelize.STRING })
self.Environment = self.sequelize.define('Environment', { name: Sequelize.STRING })
self.Environment
.belongsTo(self.Domain, { as: 'PrivateDomain', foreignKey: 'privateDomainId' })
.belongsTo(self.Domain, { as: 'PublicDomain', foreignKey: 'publicDomainId' })
self.Domain.sync({ force: true }).success(function() {
self.Environment.sync({ force: true }).success(function() {
self.Domain.create({ ip: '192.168.0.1' }).success(function(privateIp) {
self.Domain.create({ ip: '91.65.189.19' }).success(function(publicIp) {
self.Environment.create({ name: 'environment' }).success(function(env) {
env.setPrivateDomain(privateIp).success(function() {
env.setPublicDomain(publicIp).success(function() {
self.Environment.find({
where: { name: 'environment' },
include: [
{ daoFactory: self.Domain, as: 'PrivateDomain' },
{ daoFactory: self.Domain, as: 'PublicDomain' }
]
}).complete(function(err, environment) {
expect(err).to.be.null
expect(environment).to.exist
expect(environment.privateDomain).to.exist
expect(environment.privateDomain.ip).to.equal('192.168.0.1')
expect(environment.publicDomain).to.exist
expect(environment.publicDomain.ip).to.equal('91.65.189.19')
done()
})
})
})
})
})
})
})
})
})
})
describe('hasOne', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasOne(this.Task)
this.init(function() {
self.worker.setTask(self.task).success(function() {
done()
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.find({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.task).to.exist
expect(worker.task.title).to.equal('homework')
done()
})
})
})
describe('hasOne with alias', function() {
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
describe('alias', function(done) {
beforeEach(function(done) {
var self = this
this.Worker.hasOne(this.Task, { as: 'ToDo' })
this.init(function() {
self.worker.setToDo(self.task).success(function() {
done()
})
})
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.toDo).to.exist
expect(worker.toDo.title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, worker) {
expect(worker.toDo.title).to.equal('homework')
done()
})
})
})
})
describe('hasMany', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasMany(this.Task)
this.init(function() {
self.worker.setTasks([ self.task ]).success(function() {
done()
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.find({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.tasks).to.exist
expect(worker.tasks[0].title).to.equal('homework')
done()
})
})
})
describe('hasMany with alias', function() {
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
describe('alias', function() {
beforeEach(function(done) {
var self = this
this.Worker.hasMany(this.Task, { as: 'ToDos' })
this.init(function() {
self.worker.setToDos([ self.task ]).success(function() {
done()
})
})
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.find({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(err).to.be.null
expect(worker).to.exist
expect(worker.toDos).to.exist
expect(worker.toDos[0].title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.find({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDos' } ]
}).complete(function(err, worker) {
expect(worker.toDos[0].title).to.equal('homework')
done()
})
})
})
})
})
describe('queryOptions', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.user = user
done()
})
})
it("should return a DAO when queryOptions are not set", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}).done(function(err, user) {
expect(user).to.be.instanceOf(self.User.DAO)
done()
})
})
it("should return a DAO when raw is false", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}, { raw: false }).done(function(err, user) {
expect(user).to.be.instanceOf(self.User.DAO)
done()
})
})
it("should return raw data when raw is true", function(done) {
var self = this
this.User.find({ where: { username: 'barfooz'}}, { raw: true }).done(function(err, user) {
expect(user).to.not.be.instanceOf(self.User.DAO)
expect(user).to.be.instanceOf(Object)
done()
})
})
})
})
describe('findAll', function() {
describe('eager loading', function() {
describe('belongsTo', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('TaskBelongsTo', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Task.belongsTo(self.Worker)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.task.setWorker(self.worker).success(function() {
done()
})
})
})
})
})
})
it('throws an error about unexpected input if include contains a non-object', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ 1 ] })
}).to.throw(Error, 'Include unexpected. Element has to be either an instance of DAOFactory or an object.')
done()
})
it('throws an error about missing attributes if include contains an object with daoFactory', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ { daoFactory: self.Worker } ] })
}).to.throw(Error, 'Include malformed. Expected attributes: daoFactory, as!')
done()
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ self.Task ] })
}).to.throw(Error, 'TaskBelongsTo is not associated to Worker!')
done()
})
it('returns the associated worker via task.worker', function(done) {
this.Task.all({
where: { title: 'homework' },
include: [ this.Worker ]
}).complete(function(err, tasks) {
expect(err).to.be.null
expect(tasks).to.exist
expect(tasks[0].worker).to.exist
expect(tasks[0].worker.name).to.equal('worker')
done()
})
})
})
describe('hasOne', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('TaskHasOne', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasOne(self.Task)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setTaskHasOne(self.task).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.all({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to TaskHasOne!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].taskHasOne).to.exist
expect(workers[0].taskHasOne.title).to.equal('homework')
done()
})
})
})
describe('hasOne with alias', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasOne(self.Task, { as: 'ToDo' })
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setToDo(self.task).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.all({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].toDo).to.exist
expect(workers[0].toDo.title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.all({
where: { name: 'worker' },
include: [ { model: this.Task, as: 'ToDo' } ]
}).complete(function(err, workers) {
expect(workers[0].toDo.title).to.equal('homework')
done()
})
})
})
describe('hasMany', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasMany(self.Task)
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setTasks([ self.task ]).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not associated', function(done) {
var self = this
expect(function() {
self.Task.findAll({ include: [ self.Worker ] })
}).to.throw(Error, 'Worker is not associated to Task!')
done()
})
it('returns the associated tasks via worker.tasks', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ this.Task ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].tasks).to.exist
expect(workers[0].tasks[0].title).to.equal('homework')
done()
})
})
})
describe('hasMany with alias', function() {
beforeEach(function(done) {
var self = this
self.Task = self.sequelize.define('Task', { title: Sequelize.STRING })
self.Worker = self.sequelize.define('Worker', { name: Sequelize.STRING })
self.Worker.hasMany(self.Task, { as: 'ToDos' })
self.Worker.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.Worker.create({ name: 'worker' }).success(function(worker) {
self.Task.create({ title: 'homework' }).success(function(task) {
self.worker = worker
self.task = task
self.worker.setToDos([ self.task ]).success(function() {
done()
})
})
})
})
})
})
it('throws an error if included DaoFactory is not referenced by alias', function(done) {
var self = this
expect(function() {
self.Worker.findAll({ include: [ self.Task ] })
}).to.throw(Error, 'Task is not associated to Worker!')
done()
})
it('throws an error if alias is not associated', function(done) {
var self = this
expect(function() {
self.Worker.findAll({ include: [ { daoFactory: self.Task, as: 'Work' } ] })
}).to.throw(Error, 'Task (Work) is not associated to Worker!')
done()
})
it('returns the associated task via worker.task', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(err).to.be.null
expect(workers).to.exist
expect(workers[0].toDos).to.exist
expect(workers[0].toDos[0].title).to.equal('homework')
done()
})
})
it('returns the associated task via worker.task when daoFactory is aliased with model', function(done) {
this.Worker.findAll({
where: { name: 'worker' },
include: [ { daoFactory: this.Task, as: 'ToDos' } ]
}).complete(function(err, workers) {
expect(workers[0].toDos[0].title).to.equal('homework')
done()
})
})
})
describe('queryOptions', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'barfooz'}).success(function(user) {
self.user = user
done()
})
})
it("should return a DAO when queryOptions are not set", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}).done(function(err, users) {
users.forEach(function (user) {
expect(user).to.be.instanceOf(self.User.DAO)
})
done()
})
})
it("should return a DAO when raw is false", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}, { raw: false }).done(function(err, users) {
users.forEach(function (user) {
expect(user).to.be.instanceOf(self.User.DAO)
})
done()
})
})
it("should return raw data when raw is true", function(done) {
var self = this
this.User.findAll({ where: { username: 'barfooz'}}, { raw: true }).done(function(err, users) {
users.forEach(function(user) {
expect(user).to.not.be.instanceOf(self.User.DAO)
expect(users[0]).to.be.instanceOf(Object)
})
done()
})
})
})
})
describe('normal findAll', function() {
beforeEach(function(done) {
var self = this
this.User.create({username: 'user', data: 'foobar', theDate: moment().toDate()}).success(function(user) {
self.User.create({username: 'user2', data: 'bar', theDate: moment().toDate()}).success(function(user2){
self.users = [user].concat(user2)
done()
})
})
})
it("finds all entries", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).to.equal(2)
done()
})
})
it("finds all users matching the passed conditions", function(done) {
this.User.findAll({where: "id != " + this.users[1].id}).success(function(users) {
expect(users.length).to.equal(1)
done()
})
})
it("can also handle array notation", function(done) {
var self = this
this.User.findAll({where: ['id = ?', this.users[1].id]}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].id).to.equal(self.users[1].id)
done()
})
})
it("sorts the results via id in ascending order", function(done) {
this.User.findAll().success(function(users) {
expect(users.length).to.equal(2);
expect(users[0].id).to.be.below(users[1].id)
done()
})
})
it("sorts the results via id in descending order", function(done) {
this.User.findAll({ order: "id DESC" }).success(function(users) {
expect(users[0].id).to.be.above(users[1].id)
done()
})
})
it("sorts the results via a date column", function(done) {
var self = this
self.User.create({username: 'user3', data: 'bar', theDate: moment().add('hours', 2).toDate()}).success(function(){
self.User.findAll({ order: 'theDate DESC' }).success(function(users) {
expect(users[0].id).to.be.above(users[2].id)
done()
})
})
})
it("handles offset and limit", function(done) {
var self = this
this.User.bulkCreate([{username: 'bobby'}, {username: 'tables'}]).success(function() {
self.User.findAll({ limit: 2, offset: 2 }).success(function(users) {
expect(users.length).to.equal(2)
expect(users[0].id).to.equal(3)
done()
})
})
})
})
})
describe('findAndCountAll', function() {
beforeEach(function(done) {
var self = this
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'},
{username: 'bobby', data: 'foo'}
]).success(function() {
self.User.all().success(function(users){
self.users = users
done()
})
})
})
it("handles where clause [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles where clause with ordering [only]", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, order: 'id ASC'}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles offset", function(done) {
this.User.findAndCountAll({offset: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
done()
})
})
it("handles limit", function(done) {
this.User.findAndCountAll({limit: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(1)
done()
})
})
it("handles offset and limit", function(done) {
this.User.findAndCountAll({offset: 1, limit: 1}).success(function(info) {
expect(info.count).to.equal(3)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(1)
done()
})
})
})
describe('all', function() {
beforeEach(function(done) {
this.User.bulkCreate([
{username: 'user', data: 'foobar'},
{username: 'user2', data: 'bar'}
]).complete(function() {
done()
})
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).to.equal(2)
done()
})
})
})
describe('equals', function() {
it("correctly determines equality of objects", function(done) {
this.User.create({username: 'hallo', data: 'welt'}).success(function(u) {
expect(u.equals(u)).to.be.ok
done()
})
})
// sqlite can't handle multiple primary keys
if(dialect !== "sqlite") {
it("correctly determines equality with multiple primary keys", function(done) {
var userKeys = this.sequelize.define('userkeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
userKeys.sync({ force: true }).success(function() {
userKeys.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equals(u)).to.be.ok
done()
})
})
})
}
})
describe('equalsOneOf', function() {
// sqlite can't handle multiple primary keys
if (dialect !== "sqlite") {
beforeEach(function(done) {
this.userKey = this.sequelize.define('userKeys', {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING,
bio: Sequelize.TEXT
})
this.userKey.sync({ force: true }).success(function(){
done()
})
})
it('determines equality if one is matching', function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([u, {a: 1}])).to.be.ok
done()
})
})
it("doesn't determine equality if none is matching", function(done) {
this.userKey.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).success(function(u) {
expect(u.equalsOneOf([{b: 2}, {a: 1}])).to.not.be.ok
done()
})
})
}
})
describe('count', function() {
it('counts all created objects', function(done) {
var self = this
this.User.bulkCreate([{username: 'user1'}, {username: 'user2'}]).success(function() {
self.User.count().success(function(count) {
expect(count).to.equal(2)
done()
})
})
})
it('allows sql logging', function(done) {
this.User.count().on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it('filters object', function(done) {
var self = this
this.User.create({username: 'user1'}).success(function() {
self.User.create({username: 'foo'}).success(function() {
self.User.count({where: "username LIKE '%us%'"}).success(function(count) {
expect(count).to.equal(1)
done()
})
})
})
})
})
describe('min', function() {
beforeEach(function(done) {
var self = this
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function() {
self.UserWithDec.sync({ force: true }).success(function() {
done()
})
})
})
it("should return the min value", function(done) {
var self = this
this.UserWithAge.bulkCreate([{age: 3}, { age: 2 }]).success(function() {
self.UserWithAge.min('age').success(function(min) {
expect(min).to.equal(2)
done()
})
})
})
it('allows sql logging', function(done) {
this.UserWithAge.min('age').on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
it("should allow decimals in min", function(done){
var self = this
this.UserWithDec.bulkCreate([{value: 5.5}, {value: 3.5}]).success(function(){
self.UserWithDec.min('value').success(function(min){
expect(min).to.equal(3.5)
done()
})
})
})
})
describe('max', function() {
beforeEach(function(done) {
var self = this
this.UserWithAge = this.sequelize.define('UserWithAge', {
age: Sequelize.INTEGER,
order: Sequelize.INTEGER
})
this.UserWithDec = this.sequelize.define('UserWithDec', {
value: Sequelize.DECIMAL(10, 3)
})
this.UserWithAge.sync({ force: true }).success(function() {
self.UserWithDec.sync({ force: true }).success(function() {
done()
})
})
})
it("should return the max value for a field named the same as an SQL reserved keyword", function(done) {
var self = this
this.UserWithAge.bulkCreate([{age: 2, order: 3}, {age: 3, order: 5}]).success(function(){
self.UserWithAge.max('order').success(function(max) {
expect(max).to.equal(5)
done()
})
})
})
it("should return the max value", function(done) {
var self = this
self.UserWithAge.bulkCreate([{age: 2}, {age: 3}]).success(function() {
self.UserWithAge.max('age').success(function(max) {
expect(max).to.equal(3)
done()
})
})
})
it("should allow decimals in max", function(done) {
var self = this
this.UserWithDec.bulkCreate([{value: 3.5}, {value: 5.5}]).success(function(){
self.UserWithDec.max('value').success(function(max){
expect(max).to.equal(5.5)
done()
})
})
})
it('allows sql logging', function(done) {
this.UserWithAge.max('age').on('sql', function(sql) {
expect(sql).to.exist
expect(sql.toUpperCase().indexOf("SELECT")).to.be.above(-1)
done()
})
})
})
describe('schematic support', function() {
beforeEach(function(done){
var self = this;
this.UserPublic = this.sequelize.define('UserPublic', {
age: Sequelize.INTEGER
})
this.UserSpecial = this.sequelize.define('UserSpecial', {
age: Sequelize.INTEGER
})
self.sequelize.dropAllSchemas().success(function(){
self.sequelize.createSchema('schema_test').success(function(){
self.sequelize.createSchema('special').success(function(){
self.UserSpecial.schema('special').sync({force: true}).success(function(UserSpecialSync) {
self.UserSpecialSync = UserSpecialSync
done()
})
})
})
})
})
it("should be able to list schemas", function(done){
this.sequelize.showAllSchemas().success(function(schemas) {
expect(schemas).to.exist
expect(schemas[0]).to.be.instanceof(Array)
// sqlite & MySQL doesn't actually create schemas unless Model.sync() is called
// Postgres supports schemas natively
expect(schemas[0]).to.have.length((dialect === "postgres" || dialect === "postgres-native" ? 2 : 1))
done()
})
})
if (dialect === "mysql") {
it("should take schemaDelimiter into account if applicable", function(done){
var UserSpecialUnderscore = this.sequelize.define('UserSpecialUnderscore', {age: Sequelize.INTEGER}, {schema: 'hello', schemaDelimiter: '_'})
var UserSpecialDblUnderscore = this.sequelize.define('UserSpecialDblUnderscore', {age: Sequelize.INTEGER})
UserSpecialUnderscore.sync({force: true}).success(function(User){
UserSpecialDblUnderscore.schema('hello', '__').sync({force: true}).success(function(DblUser){
DblUser.create({age: 3}).on('sql', function(dblSql){
User.create({age: 3}).on('sql', function(sql){
expect(dblSql).to.exist
expect(dblSql.indexOf('INSERT INTO `hello__UserSpecialDblUnderscores`')).to.be.above(-1)
expect(sql).to.exist
expect(sql.indexOf('INSERT INTO `hello_UserSpecialUnderscores`')).to.be.above(-1)
done()
})
})
})
})
})
}
it("should be able to create and update records under any valid schematic", function(done){
var self = this
self.UserPublic.sync({ force: true }).success(function(UserPublicSync){
UserPublicSync.create({age: 3}).on('sql', function(UserPublic){
self.UserSpecialSync.schema('special').create({age: 3})
.on('sql', function(UserSpecial){
expect(UserSpecial).to.exist
expect(UserPublic).to.exist
if (dialect === "postgres") {
expect(self.UserSpecialSync.getTableName()).to.equal('"special"."UserSpecials"');
expect(UserSpecial.indexOf('INSERT INTO "special"."UserSpecials"')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO "UserPublics"')).to.be.above(-1)
} else if (dialect === "sqlite") {
expect(self.UserSpecialSync.getTableName()).to.equal('`special`.`UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
} else {
expect(self.UserSpecialSync.getTableName()).to.equal('`special.UserSpecials`');
expect(UserSpecial.indexOf('INSERT INTO `special.UserSpecials`')).to.be.above(-1)
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1)
}
})
.success(function(UserSpecial){
UserSpecial.updateAttributes({age: 5})
.on('sql', function(user){
expect(user).to.exist
if (dialect === "postgres") {
expect(user.indexOf('UPDATE "special"."UserSpecials"')).to.be.above(-1)
} else {
expect(user.indexOf('UPDATE `special.UserSpecials`')).to.be.above(-1)
}
done()
})
})
})
})
})
})
describe('references', function() {
this.timeout(3000)
beforeEach(function(done) {
var self = this
self.Author = self.sequelize.define('author', { firstName: Sequelize.STRING })
self.Author.sync({ force: true }).success(function() {
done()
})
})
afterEach(function(done) {
var self = this
self.sequelize.getQueryInterface().dropTable('posts', { force: true }).success(function() {
self.sequelize.getQueryInterface().dropTable('authors', { force: true }).success(function() {
done()
})
})
})
it('uses an existing dao factory and references the author table', function(done) {
var self = this
, Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: this.Author,
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
}
else if (dialect === 'mysql') {
expect(sql).to.match(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
}
else if (dialect === 'sqlite') {
expect(sql).to.match(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
it('uses a table name as a string and references the author table', function(done) {
var self = this
, Post = self.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: 'authors',
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().on('sql', function(sql) {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/)
}
else if (dialect === 'mysql') {
expect(sql).to.match(/FOREIGN KEY \(`authorId`\) REFERENCES `authors` \(`id`\)/)
}
else if (dialect === 'sqlite') {
expect(sql).to.match(/`authorId` INTEGER REFERENCES `authors` \(`id`\)/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
it("emits an error event as the referenced table name is invalid", function(done) {
var self = this
, Post = this.sequelize.define('post', {
title: Sequelize.STRING,
authorId: {
type: Sequelize.INTEGER,
references: '4uth0r5',
referencesKey: "id"
}
})
this.Author.hasMany(Post)
Post.belongsTo(this.Author)
Post.sync().success(function() {
if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).to.equal(1)
done()
} else {
// the parser should not end up here ...
expect(2).to.equal(1)
done()
}
}).error(function(err) {
if (dialect === 'mysql') {
expect(err.message).to.match(/ER_CANNOT_ADD_FOREIGN|ER_CANT_CREATE_TABLE/)
}
else if (dialect === 'sqlite') {
// the parser should not end up here ... see above
expect(1).to.equal(2)
}
else if (dialect === 'postgres') {
expect(err.message).to.match(/relation "4uth0r5" does not exist/)
} else {
throw new Error('Undefined dialect!')
}
done()
})
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAO"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1,20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe('Escaping', function() {
it('is done properly for special characters', function(done) {
// Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a"
// But this causes sqlite to fail and exits the entire test suite immediately
var bio = dialect + "'\"\n" // Need to add the dialect here so in case of failure I know what DB it failed for
, self = this
this.User.create({ username: bio }).success(function(u1) {
self.User.find(u1.id).success(function(u2) {
expect(u2.username).to.equal(bio)
done()
})
})
})
})
describe('isNewRecord', function() {
it('returns true for non-saved objects', function(done) {
var user = this.User.build({ username: 'user' })
expect(user.id).to.be.null
expect(user.isNewRecord).to.be.ok
done()
})
it("returns false for saved objects", function(done) {
this.User.build({ username: 'user' }).save().success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
it("returns false for created objects", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
it("returns false for objects found by find method", function(done) {
var self = this
this.User.create({ username: 'user' }).success(function() {
self.User.create({ username: 'user' }).success(function(user) {
self.User.find(user.id).success(function(user) {
expect(user.isNewRecord).to.not.be.ok
done()
})
})
})
})
it("returns false for objects found by findAll method", function(done) {
var self = this
, users = []
for (var i = 0; i < 10; i++) {
users[users.length] = {username: 'user'}
}
this.User.bulkCreate(users).success(function() {
self.User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isNewRecord).to.not.be.ok
})
done()
})
})
})
})
describe('increment', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(function(){
done()
})
})
it('with array', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(2)
done()
})
})
})
})
it('with single field', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment('aNumber', 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(2)
done()
})
})
})
})
it('should still work right with other concurrent updates', function(done) {
var self = this
this.User.find(1).complete(function (err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).complete(function (err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).complete(function () {
user1.increment(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user5) {
expect(user5.aNumber).to.be.equal(3)
done()
})
})
})
})
})
})
it('should still work right with other concurrent increments', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
var _done = _.after(3, function() {
self.User.find(1).complete(function(err, user2) {
expect(user2.aNumber).to.equal(6)
done()
})
})
user1.increment(['aNumber'], 2).complete(_done)
user1.increment(['aNumber'], 2).complete(_done)
user1.increment(['aNumber'], 2).complete(_done)
})
})
it('with key value pair', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.increment({ 'aNumber': 1, 'bNumber': 2}).success(function() {
self.User.find(1).complete(function (err, user3) {
expect(user3.aNumber).to.be.equal(1)
expect(user3.bNumber).to.be.equal(2)
done()
})
})
})
})
})
describe('decrement', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(done)
})
it('with array', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-2)
done()
})
})
})
})
it('with single field', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement('aNumber', 2).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-2)
done()
})
})
})
})
it('should still work right with other concurrent updates', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
// Select the user again (simulating a concurrent query)
self.User.find(1).complete(function(err, user2) {
user2.updateAttributes({
aNumber: user2.aNumber + 1
}).complete(function () {
user1.decrement(['aNumber'], 2).complete(function() {
self.User.find(1).complete(function(err, user5) {
expect(user5.aNumber).to.be.equal(-1)
done()
})
})
})
})
})
})
it('should still work right with other concurrent increments', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
var _done = _.after(3, function() {
self.User.find(1).complete(function (err, user2) {
expect(user2.aNumber).to.equal(-6)
done()
})
})
user1.decrement(['aNumber'], 2).complete(_done)
user1.decrement(['aNumber'], 2).complete(_done)
user1.decrement(['aNumber'], 2).complete(_done)
})
})
it('with key value pair', function(done) {
var self = this
this.User.find(1).complete(function(err, user1) {
user1.decrement({ 'aNumber': 1, 'bNumber': 2}).complete(function() {
self.User.find(1).complete(function(err, user3) {
expect(user3.aNumber).to.be.equal(-1)
expect(user3.bNumber).to.be.equal(-2)
done()
})
})
})
})
})
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function(done) {
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
originalUser.updateAttributes({ username: 'Doe John' }).complete(function() {
originalUser.reload().complete(function (err, updatedUser) {
expect(originalUser === updatedUser).to.be.true
done()
})
})
})
})
it("should update the values on all references to the DAO", function(done) {
var self = this
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
self.User.find(originalUser.id).complete(function(err, updater) {
updater.updateAttributes({ username: 'Doe John' }).complete(function() {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).to.equal('John Doe')
originalUser.reload().complete(function(err, updatedUser) {
expect(originalUser.username).to.equal('Doe John')
expect(updatedUser.username).to.equal('Doe John')
done()
})
})
})
})
})
it("should update read only attributes as well (updatedAt)", function(done) {
var self = this
this.timeout = 2000
this.User.create({ username: 'John Doe' }).complete(function(err, originalUser) {
var originallyUpdatedAt = originalUser.updatedAt
// Wait for a second, so updatedAt will actually be different
setTimeout(function () {
self.User.find(originalUser.id).complete(function(err, updater) {
updater.updateAttributes({ username: 'Doe John' }).complete(function () {
originalUser.reload().complete(function(err, updatedUser) {
expect(originalUser.updatedAt).to.be.above(originallyUpdatedAt)
expect(updatedUser.updatedAt).to.be.above(originallyUpdatedAt)
done()
})
})
})
}, 1000)
})
})
it("should update the associations as well", function(done) {
var Book = this.sequelize.define('Book', { title: DataTypes.STRING })
, Page = this.sequelize.define('Page', { content: DataTypes.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
Book.sync().success(function() {
Page.sync().success(function() {
Book.create({ title: 'A very old book' }).success(function(book) {
Page.create({ content: 'om nom nom' }).success(function(page) {
book.setPages([ page ]).success(function() {
Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).success(function(leBook) {
page.updateAttributes({ content: 'something totally different' }).success(function(page) {
expect(leBook.pages[0].content).to.equal('om nom nom')
expect(page.content).to.equal('something totally different')
leBook.reload().success(function(leBook) {
expect(leBook.pages[0].content).to.equal('something totally different')
expect(page.content).to.equal('something totally different')
done()
})
})
})
})
})
})
})
})
})
})
describe('default values', function() {
describe('current date', function() {
it('should store a date in touchedAt', function(done) {
var user = this.User.build({ username: 'a user'})
expect(user.touchedAt).to.be.instanceof(Date)
done()
})
it("should store the current date in touchedAt", function(done) {
var clock = sinon.useFakeTimers()
clock.tick(5000)
var user = this.User.build({ username: 'a user'})
clock.restore()
expect(+user.touchedAt).to.be.equal(5000)
done()
})
})
describe('allowNull date', function() {
it('should be just "null" and not Date with Invalid Date', function(done) {
var self = this
this.User.build({ username: 'a user'}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue).to.be.null
done()
})
})
})
it('should be the same valid date when saving the date', function(done) {
var self = this
var date = new Date()
this.User.build({ username: 'a user', dateAllowNullTrue: date}).save().success(function() {
self.User.find({where: {username: 'a user'}}).success(function(user) {
expect(user.dateAllowNullTrue.toString()).to.equal(date.toString())
done()
})
})
})
})
})
describe('complete', function() {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).complete(function(err) {
expect(err).to.exist
expect(err.message).to.exist
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().complete(function(err, result) {
expect(err).to.be.null
expect(result).to.exist
done()
})
})
})
describe('save', function() {
this.timeout(3000) // for update timestamp
it('only updates fields in passed array', function(done) {
var self = this
, userId = null
, date = new Date(1990, 01, 01)
this.User.create({
username: 'foo',
touchedAt: new Date()
}).success(function(user) {
user.username = 'fizz'
user.touchedAt = date
user.save(['username']).success(function(){
// re-select user
self.User.find(user.id).success(function(user2) {
// name should have changed
expect(user2.username).to.equal('fizz')
// bio should be unchanged
expect(user2.birthDate).not.to.equal(date)
done()
})
})
})
})
it("stores an entry in the database", function(done) {
var username = 'user'
, User = this.User
, user = this.User.build({
username: username,
touchedAt: new Date(1984, 8, 23)
})
User.all().success(function(users) {
expect(users).to.have.length(0)
user.save().success(function(){
User.all().success(function(users) {
expect(users).to.have.length(1)
expect(users[0].username).to.equal(username)
expect(users[0].touchedAt).to.be.instanceof(Date)
expect(users[0].touchedAt).to.equalDate(new Date(1984, 8, 23))
done()
})
})
})
})
it("updates the timestamps", function(done) {
var now = Date.now()
, user = null
, updatedAt = null
, User = this.User
// timeout is needed, in order to check the update of the timestamp
var build = function(callback) {
user = User.build({ username: 'user' })
updatedAt = user.updatedAt
expect(updatedAt.getTime()).to.be.above(now)
setTimeout(function() {
user.save().success(function() {
expect(updatedAt.getTime()).to.be.below(user.updatedAt.getTime())
callback()
})
}, 1000)
}
// closures are fun :)
setTimeout(function() {
build(function() {
done()
})
}, 1000)
})
describe('without timestamps option', function() {
it("doesn't update the updatedAt column", function(done) {
var User2 = this.sequelize.define('User2', {
username: DataTypes.STRING,
updatedAt: DataTypes.DATE
}, { timestamps: false })
User2.sync().success(function() {
User2.create({ username: 'john doe' }).success(function(johnDoe) {
// sqlite and mysql return undefined, whereas postgres returns null
expect([undefined, null].indexOf(johnDoe.updatedAt)).not.to.be.equal(-1)
done()
})
})
})
})
it('should fail a validation upon creating', function(done){
this.User.create({aNumber: 0, validateTest: 'hello'}).error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.above(-1)
done()
})
})
it('should fail a validation upon building', function(done){
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateCustom).to.exist
expect(err.validateCustom).to.be.instanceof(Array)
expect(err.validateCustom[0]).to.exist
expect(err.validateCustom[0]).to.equal('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done){
this.User.create({aNumber: 0}).success(function(user){
user.updateAttributes({validateTest: 'hello'}).error(function(err){
expect(err).to.exist
expect(err).to.be.instanceof(Object)
expect(err.validateTest).to.exist
expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.above(-1)
done()
})
})
})
it('takes zero into account', function(done) {
this.User.build({ aNumber: 0 }).save([ 'aNumber' ]).success(function(user) {
expect(user.aNumber).to.equal(0)
done()
})
})
it('saves a record with no primary key', function(done){
var HistoryLog = this.sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
HistoryLog.sync().success(function() {
HistoryLog.create({ someText: 'Some random text', aNumber: 3, aRandomId: 5 }).success(function(log) {
log.updateAttributes({ aNumber: 5 }).success(function(newLog){
expect(newLog.aNumber).to.equal(5)
done()
})
})
})
})
describe('eagerly loaded objects', function() {
beforeEach(function(done) {
var self = this
this.UserEager = this.sequelize.define('UserEagerLoadingSaves', {
username: DataTypes.STRING,
age: DataTypes.INTEGER
}, { timestamps: false })
this.ProjectEager = this.sequelize.define('ProjectEagerLoadingSaves', {
title: DataTypes.STRING,
overdue_days: DataTypes.INTEGER
}, { timestamps: false })
this.UserEager.hasMany(this.ProjectEager, { as: 'Projects' })
this.ProjectEager.belongsTo(this.UserEager, { as: 'Poobah' })
self.UserEager.sync({force: true}).success(function() {
self.ProjectEager.sync({force: true}).success(function() {
done()
})
})
})
it('saves one object that has a collection of eagerly loaded objects', function(done) {
var self = this
this.UserEager.create({ username: 'joe', age: 1 }).success(function(user) {
self.ProjectEager.create({ title: 'project-joe1', overdue_days: 0 }).success(function(project1) {
self.ProjectEager.create({ title: 'project-joe2', overdue_days: 0 }).success(function(project2) {
user.setProjects([project1, project2]).success(function() {
self.UserEager.find({where: {age: 1}, include: [{model: self.ProjectEager, as: 'Projects'}]}).success(function(user) {
expect(user.username).to.equal('joe')
expect(user.age).to.equal(1)
expect(user.projects).to.exist
expect(user.projects.length).to.equal(2)
user.age = user.age + 1 // happy birthday joe
user.save().success(function() {
expect(user.username).to.equal('joe')
expect(user.age).to.equal(2)
expect(user.projects).to.exist
expect(user.projects.length).to.equal(2)
done()
})
})
})
})
})
})
})
it('saves many objects that each a have collection of eagerly loaded objects', function(done) {
var self = this
this.UserEager.create({ username: 'bart', age: 20 }).success(function(bart) {
self.UserEager.create({ username: 'lisa', age: 20 }).success(function(lisa) {
self.ProjectEager.create({ title: 'detention1', overdue_days: 0 }).success(function(detention1) {
self.ProjectEager.create({ title: 'detention2', overdue_days: 0 }).success(function(detention2) {
self.ProjectEager.create({ title: 'exam1', overdue_days: 0 }).success(function(exam1) {
self.ProjectEager.create({ title: 'exam2', overdue_days: 0 }).success(function(exam2) {
bart.setProjects([detention1, detention2]).success(function() {
lisa.setProjects([exam1, exam2]).success(function() {
self.UserEager.findAll({where: {age: 20}, order: 'username ASC', include: [{model: self.ProjectEager, as: 'Projects'}]}).success(function(simpsons) {
var _bart, _lisa
expect(simpsons.length).to.equal(2)
_bart = simpsons[0]
_lisa = simpsons[1]
expect(_bart.projects).to.exist
expect(_lisa.projects).to.exist
expect(_bart.projects.length).to.equal(2)
expect(_lisa.projects.length).to.equal(2)
_bart.age = _bart.age + 1 // happy birthday bart - off to Moe's
_bart.save().success(function(savedbart) {
expect(savedbart.username).to.equal('bart')
expect(savedbart.age).to.equal(21)
_lisa.username = 'lsimpson'
_lisa.save().success(function(savedlisa) {
expect(savedlisa.username).to.equal('lsimpson')
expect(savedlisa.age).to.equal(20)
done()
})
})
})
})
})
})
})
})
})
})
})
})
it('saves many objects that each has one eagerly loaded object (to which they belong)', function(done) {
var self = this
this.UserEager.create({ username: 'poobah', age: 18 }).success(function(user) {
self.ProjectEager.create({ title: 'homework', overdue_days: 10 }).success(function(homework) {
self.ProjectEager.create({ title: 'party', overdue_days: 2 }).success(function(party) {
user.setProjects([homework, party]).success(function() {
self.ProjectEager.findAll({include: [{model: self.UserEager, as: 'Poobah'}]}).success(function(projects) {
expect(projects.length).to.equal(2)
expect(projects[0].poobah).to.exist
expect(projects[1].poobah).to.exist
expect(projects[0].poobah.username).to.equal('poobah')
expect(projects[1].poobah.username).to.equal('poobah')
projects[0].title = 'partymore'
projects[1].title = 'partymore'
projects[0].overdue_days = 0
projects[1].overdue_days = 0
projects[0].save().success(function() {
projects[1].save().success(function() {
self.ProjectEager.findAll({where: {title: 'partymore', overdue_days: 0}, include: [{model: self.UserEager, as: 'Poobah'}]}).success(function(savedprojects) {
expect(savedprojects.length).to.equal(2)
expect(savedprojects[0].poobah).to.exist
expect(savedprojects[1].poobah).to.exist
expect(savedprojects[0].poobah.username).to.equal('poobah')
expect(savedprojects[1].poobah.username).to.equal('poobah')
done()
})
})
})
})
})
})
})
})
})
})
})
describe('toJSON', function() {
beforeEach(function(done) {
var self = this
this.User = this.sequelize.define('UserWithUsernameAndAgeAndIsAdmin', {
username: DataTypes.STRING,
age: DataTypes.INTEGER,
isAdmin: DataTypes.BOOLEAN
}, { timestamps: false })
this.Project = this.sequelize.define('NiceProject', { title: DataTypes.STRING }, { timestamps: false })
this.User.hasMany(this.Project, { as: 'Projects' })
this.Project.belongsTo(this.User, { as: 'LovelyUser' })
this.User.sync({ force: true }).success(function() {
self.Project.sync({ force: true }).success(function() {
done()
})
})
})
it('returns an object containing all values', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(user.toJSON()).to.deep.equal({ username: 'test.user', age: 99, isAdmin: true, id: null })
done()
})
it('returns a response that can be stringified', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.stringify(user)).to.deep.equal('{"username":"test.user","age":99,"isAdmin":true,"id":null}')
done()
})
it('returns a response that can be stringified and then parsed', function(done) {
var user = this.User.build({ username: 'test.user', age: 99, isAdmin: true })
expect(JSON.parse(JSON.stringify(user))).to.deep.equal({ username: 'test.user', age: 99, isAdmin: true, id: null })
done()
})
it('includes the eagerly loaded associations', function(done) {
var self = this
this.User.create({ username: 'fnord', age: 1, isAdmin: true }).success(function(user) {
self.Project.create({ title: 'fnord' }).success(function(project) {
user.setProjects([ project ]).success(function() {
self.User.findAll({include: [ { model: self.Project, as: 'Projects' } ]}).success(function(users) {
var _user = users[0]
expect(_user.projects).to.exist
expect(JSON.parse(JSON.stringify(_user)).projects).to.exist
self.Project.findAll({include: [ { model: self.User, as: 'LovelyUser' } ]}).success(function(projects) {
var _project = projects[0]
expect(_project.lovelyUser).to.exist
expect(JSON.parse(JSON.stringify(_project)).lovelyUser).to.exist
done()
})
})
})
})
})
})
})
describe('findAll', function() {
beforeEach(function(done) {
this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, { paranoid: true })
this.ParanoidUser.hasOne(this.ParanoidUser)
this.ParanoidUser.sync({ force: true }).success(function() {
done()
})
})
it("escapes a single single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user'name" })
.success(function() {
self.User.findAll({
where: { username: "user'name" }
}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("user'name")
done()
})
})
})
it("escapes two single quotes properly in where clauses", function(done) {
var self = this
this.User
.create({ username: "user''name" })
.success(function() {
self.User.findAll({
where: { username: "user''name" }
}).success(function(users) {
expect(users.length).to.equal(1)
expect(users[0].username).to.equal("user''name")
done()
})
})
})
it("returns the timestamps if no attributes have been specified", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
self.User.findAll().success(function(users) {
expect(users[0].createdAt).to.exist
done()
})
})
})
it("does not return the timestamps if the username attribute has been specified", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
self.User.findAll({ attributes: ['username'] }).success(function(users) {
expect(users[0].createdAt).not.to.exist
expect(users[0].username).to.exist
done()
})
})
})
it("creates the deletedAt property, when defining paranoid as true", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
expect(users[0].deletedAt).to.be.null
done()
})
})
})
it("sets deletedAt property to a specific date when deleting an instance", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
users[0].destroy().success(function(user) {
expect(user.deletedAt.getMonth).to.exist
done()
})
})
})
})
it("keeps the deletedAt-attribute with value null, when running updateAttributes", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
users[0].updateAttributes({username: 'newFnord'}).success(function(user) {
expect(user.deletedAt).to.be.null
done()
})
})
})
})
it("keeps the deletedAt-attribute with value null, when updating associations", function(done) {
var self = this
this.ParanoidUser.create({ username: 'fnord' }).success(function() {
self.ParanoidUser.findAll().success(function(users) {
self.ParanoidUser.create({ username: 'linkedFnord' }).success(function(linkedUser) {
users[0].setParanoidUser( linkedUser ).success(function(user) {
expect(user.deletedAt).to.be.null
done()
})
})
})
})
})
it("can reuse query option objects", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
self.User.findAll(query).success(function(users) {
expect(users[0].username).to.equal('fnord')
self.User.findAll(query).success(function(users) {
expect(users[0].username).to.equal('fnord')
done()
})
})
})
})
})
describe('find', function() {
it("can reuse query option objects", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function() {
var query = { where: { username: 'fnord' }}
self.User.find(query).success(function(user) {
expect(user.username).to.equal('fnord')
self.User.find(query).success(function(user) {
expect(user.username).to.equal('fnord')
done()
})
})
})
})
})
describe('equals', function() {
it("can compare records with Date field", function(done) {
var self = this
this.User.create({ username: 'fnord' }).success(function(user1) {
var query = { where: { username: 'fnord' }}
self.User.find(query).success(function(user2) {
expect(user1.equals(user2)).to.be.true
done()
})
})
})
})
describe('values', function() {
it('returns all values', function(done) {
var User = this.sequelize.define('UserHelper', {
username: DataTypes.STRING
}, { timestamps: false, logging: false })
User.sync().success(function() {
var user = User.build({ username: 'foo' })
expect(user.values).to.deep.equal({ username: "foo", id: null })
done()
})
})
})
describe('updateAttributes', function() {
it("updates attributes in the database", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.username).to.equal('user')
user.updateAttributes({ username: 'person' }).success(function(user) {
expect(user.username).to.equal('person')
done()
})
})
})
it("ignores unknown attributes", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.updateAttributes({ username: 'person', foo: 'bar'}).success(function(user) {
expect(user.username).to.equal('person')
expect(user.foo).not.to.exist
done()
})
})
})
it("doesn't update primary keys or timestamps", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
bio: DataTypes.TEXT,
identifier: {type: DataTypes.STRING, primaryKey: true}
})
User.sync({ force: true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var oldCreatedAt = user.createdAt
, oldIdentifier = user.identifier
user.updateAttributes({
name: 'foobar',
createdAt: new Date(2000, 1, 1),
identifier: 'another identifier'
}).success(function(user) {
expect((new Date(user.createdAt)).getTime()).to.equal((new Date(oldCreatedAt)).getTime())
expect(user.identifier).to.equal(oldIdentifier)
done()
})
})
})
})
it("uses primary keys in where clause", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: DataTypes.STRING,
bio: DataTypes.TEXT,
identifier: {type: DataTypes.STRING, primaryKey: true}
})
User.sync({ force: true }).success(function(){
User.create({
name: 'snafu',
identifier: 'identifier'
}).success(function(user) {
var emitter = user.updateAttributes({name: 'foobar'})
emitter.on('sql', function(sql) {
expect(sql).to.match(/WHERE [`"]identifier[`"]..identifier./)
done()
})
})
})
})
it('stores and restores null values', function(done) {
var Download = this.sequelize.define('download', {
startedAt: DataTypes.DATE,
canceledAt: DataTypes.DATE,
finishedAt: DataTypes.DATE
})
Download.sync().success(function() {
Download.create({
startedAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt).to.not.be.ok
expect(download.finishedAt).to.not.be.ok
download.updateAttributes({
canceledAt: new Date()
}).success(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt instanceof Date).to.be.true
expect(download.finishedAt).to.not.be.ok
Download.all({
where: (dialect === 'postgres' ? '"finishedAt" IS NULL' : "`finishedAt` IS NULL")
}).success(function(downloads) {
downloads.forEach(function(download) {
expect(download.startedAt instanceof Date).to.be.true
expect(download.canceledAt instanceof Date).to.be.true
expect(download.finishedAt).to.not.be.ok
done()
})
})
})
})
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
var chai = require('chai')
, expect = chai.expect
, Sequelize = require(__dirname + '/../index')
, Support = require(__dirname + '/support')
, config = require(__dirname + '/config/config')
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
describe(Support.getTestDialectTeaser("DaoValidator"), function() {
describe('validations', function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
onComplete: function(sequelize) {
this.sequelize = sequelize
done()
}.bind(this)
})
}) //- before
var checks = {
is: {
spec: { args: ["[a-z]",'i'] },
......@@ -116,72 +104,72 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
fail: "a",
pass: "0"
}
, len : {
, len: {
spec: { args: [2,4] },
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, len: {
, len$: {
spec: [2,4],
fail: ["1", "12345"],
pass: ["12", "123", "1234"],
raw: true
}
, isUUID : {
, isUUID: {
spec: { args: 4 },
fail: "f47ac10b-58cc-3372-a567-0e02b2c3d479",
pass: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
, isDate : {
, isDate: {
fail: "not a date",
pass: "2011-02-04"
}
, isAfter : {
, isAfter: {
spec: { args: "2011-11-05" },
fail: "2011-11-04",
pass: "2011-11-05"
}
, isBefore : {
, isBefore: {
spec: { args: "2011-11-05" },
fail: "2011-11-06",
pass: "2011-11-05"
}
, isIn : {
, isIn: {
spec: { args: "abcdefghijk" },
fail: "ghik",
pass: "ghij"
}
, notIn : {
, notIn: {
spec: { args: "abcdefghijk" },
fail: "ghij",
pass: "ghik"
}
, max : {
, max: {
spec: { args: 23 },
fail: "24",
pass: "23"
}
, max : {
, max$: {
spec: 23,
fail: "24",
pass: "23"
}
, min : {
, min: {
spec: { args: 23 },
fail: "22",
pass: "23"
}
, min : {
, min$: {
spec: 23,
fail: "22",
pass: "23"
}
, isArray : {
, isArray: {
fail: 22,
pass: [22]
}
, isCreditCard : {
, isCreditCard: {
fail: "401288888888188f",
pass: "4012888888881881"
}
......@@ -189,6 +177,8 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var validator in checks) {
if (checks.hasOwnProperty(validator)) {
validator = validator.replace(/\$$/, '')
var validatorDetails = checks[validator]
if (!validatorDetails.hasOwnProperty("raw")) {
......@@ -214,7 +204,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
validations[validator].msg = message
var UserFail = this.sequelize.define('User' + Math.random(), {
var UserFail = this.sequelize.define('User' + config.rand(), {
name: {
type: Sequelize.STRING,
validate: validations
......@@ -222,20 +212,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
})
var failingUser = UserFail.build({ name : failingValue })
, errors = undefined;
, errors = undefined
failingUser.validate().done( function(err,_errors) {
expect(_errors).not.toBeNull();
expect(_errors).toEqual({ name : [message] });
done();
});
});
expect(_errors).to.not.be.null
expect(_errors).to.deep.eql({ name : [message] })
done()
})
})
}
////////////////////////////
// test the success cases //
////////////////////////////
for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j]
......@@ -250,29 +240,28 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
validations[validator].msg = validator + "(" + succeedingValue + ")"
var UserSuccess = this.sequelize.define('User' + Math.random(), {
var UserSuccess = this.sequelize.define('User' + config.rand(), {
name: {
type: Sequelize.STRING,
validate: validations
}
})
var successfulUser = UserSuccess.build({ name: succeedingValue });
var successfulUser = UserSuccess.build({ name: succeedingValue })
successfulUser.validate().success( function() {
expect(arguments.length).toBe(0);
done();
expect(arguments).to.have.length(0)
done()
}).error(function(err) {
expect(err).toBe({})
done();
});
expect(err).to.be.deep.equal({})
done()
})
})
}
}
}
it('correctly validates using custom validation methods', function(done) {
var User = this.sequelize.define('User' + Math.random(), {
var User = this.sequelize.define('User' + config.rand(), {
name: {
type: Sequelize.STRING,
validate: {
......@@ -290,22 +279,21 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = User.build({ name : "3" })
failingUser.validate().success(function(errors) {
expect(errors).toEqual({ name: ["name should equal '2'"] })
expect(errors).to.deep.equal({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" })
successfulUser.validate().success(function() {
expect(arguments.length).toBe(0);
done();
expect(arguments).to.have.length(0)
done()
}).error(function(err) {
expect(err).toBe({});
done();
expect(err).to.deep.equal({})
done()
})
})
});
})
it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + Math.random(), {
var User = this.sequelize.define('User' + config.rand(), {
age: {
type: Sequelize.INTEGER,
allowNull: true,
......@@ -319,8 +307,8 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
.build({ age: -1 })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ age: ['must be positive'] })
expect(errors).not.to.be.null
expect(errors).to.deep.equal({ age: ['must be positive'] })
User.build({ age: null }).validate().success(function() {
User.build({ age: 1 }).validate().success(function() {
......@@ -331,7 +319,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
})
it('validates a model with custom model-wide validation methods', function(done) {
var Foo = this.sequelize.define('Foo' + Math.random(), {
var Foo = this.sequelize.define('Foo' + config.rand(), {
field1: {
type: Sequelize.INTEGER,
allowNull: true
......@@ -356,38 +344,37 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
.build({ field1: null, field2: null })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull()
expect(errors).toEqual({ 'xnor': ['xnor failed'] })
expect(errors).not.to.be.null
expect(errors).to.deep.equal({ 'xnor': ['xnor failed'] })
Foo
.build({ field1: 33, field2: null })
.validate()
.success(function(errors) {
expect(errors).not.toBeDefined()
expect(errors).not.exist
done()
})
})
})
it('validates model with a validator whose arg is an Array successfully twice in a row', function(done){
var Foo = this.sequelize.define('Foo' + Math.random(), {
var Foo = this.sequelize.define('Foo' + config.rand(), {
bar: {
type: Sequelize.STRING,
validate: {
isIn: [['a', 'b']]
}
}
}), foo;
}), foo
foo = Foo
.build({bar:'a'});
foo = Foo.build({bar:'a'})
foo.validate().success(function(errors){
expect(errors).not.toBeDefined()
expect(errors).not.to.exist
foo.validate().success(function(errors){
expect(errors).not.toBeDefined()
done();
});
});
});
expect(errors).not.to.exist
done()
})
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
var chai = require('chai')
, expect = chai.expect
, Sequelize = require(__dirname + '/../index')
, Support = require(__dirname + '/support')
buster.spec.expose()
chai.Assertion.includeStack = true
describe(Helpers.getTestDialectTeaser('DataTypes'), function() {
it('should return DECIMAL for the default decimal type', function() {
expect(Sequelize.DECIMAL).toEqual('DECIMAL');
});
describe(Support.getTestDialectTeaser('DataTypes'), function() {
it('should return DECIMAL for the default decimal type', function(done) {
expect(Sequelize.DECIMAL.toString()).to.equal('DECIMAL')
done()
})
it('should return DECIMAL(10,2) for the default decimal type with arguments', function() {
expect(Sequelize.DECIMAL(10, 2)).toEqual('DECIMAL(10,2)');
});
it('should return DECIMAL(10,2) for the default decimal type with arguments', function(done) {
expect(Sequelize.DECIMAL(10, 2)).to.equal('DECIMAL(10,2)')
done()
})
var tests = [
[Sequelize.STRING, 'STRING', 'VARCHAR(255)'],
......@@ -59,8 +59,9 @@ describe(Helpers.getTestDialectTeaser('DataTypes'), function() {
]
tests.forEach(function(test) {
it('transforms "' + test[1] + '" to "' + test[2] + '"', function() {
expect(test[0]).toEqual(test[2])
it('transforms "' + test[1] + '" to "' + test[2] + '"', function(done) {
expect(test[0].toString()).to.equal(test[2])
done()
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, sinon = require('sinon')
, CustomEventEmitter = require("../../lib/emitters/custom-event-emitter")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() {
describe(Support.getTestDialectTeaser("CustomEventEmitter"), function () {
describe("proxy", function () {
/* Tests could _probably_ be run synchronously, but for future proofing we're basing it on the events */
it("should correctly work with success listeners", function (done) {
it("should correctly work with success listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, success = this.spy()
, success = sinon.spy()
emitter.success(success)
proxy.success(function () {
process.nextTick(function () {
expect(success.called).toEqual(true)
done();
expect(success.called).to.be.true
done()
})
})
......@@ -29,16 +26,16 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() {
proxy.emit('success')
})
it("should correctly work with error listeners", function (done) {
it("should correctly work with error listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, error = this.spy()
, error = sinon.spy()
emitter.error(error)
proxy.error(function () {
process.nextTick(function () {
expect(error.called).toEqual(true)
done();
proxy.error(function() {
process.nextTick(function() {
expect(error.called).to.be.true
done()
})
})
......@@ -46,21 +43,21 @@ describe(Helpers.getTestDialectTeaser("CustomEventEmitter"), function() {
proxy.emit('error')
})
it("should correctly work with complete/done listeners", function (done) {
it("should correctly work with complete/done listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, complete = this.spy()
, complete = sinon.spy()
emitter.complete(complete)
proxy.complete(function () {
process.nextTick(function () {
expect(complete.called).toEqual(true)
done();
proxy.complete(function() {
process.nextTick(function() {
expect(complete.called).to.be.true
done()
})
})
proxy.proxy(emitter)
proxy.emit('success')
})
});
});
\ No newline at end of file
})
})
var chai = require('chai')
, expect = chai.expect
, Sequelize = require(__dirname + '/../index')
, Support = require(__dirname + '/support')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Language Util"), function() {
before(function(done) {
this.sequelize.options.language = 'es'
done()
})
after(function(done) {
this.sequelize.options.language = 'en'
done()
})
describe("Plural", function(){
it("should rename tables to their plural form...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
expect(table.tableName).to.equal('arboles')
expect(table2.tableName).to.equal('androides')
done()
})
it("should be able to pluralize/singularize associations...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
, table3 = self.sequelize.define('hombre', {name: Sequelize.STRING})
table.hasOne(table2)
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androides.identifier).to.equal('arbolId')
expect(table2.associations.arboles).to.exist
expect(table3.associations.androideshombres).to.exist
done()
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, QueryChainer = require("../lib/query-chainer")
, Migrator = require("../lib/migrator")
, dialect = Support.getTestDialect()
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Migrator"), function() {
beforeEach(function() {
this.init = function(options, callback) {
options = Support.Sequelize.Utils._.extend({
path: __dirname + '/assets/migrations',
logging: function(){}
}, options || {})
var migrator = new Migrator(this.sequelize, options)
migrator
.findOrCreateSequelizeMetaDAO({ force: true })
.success(function(SequelizeMeta) {
callback && callback(migrator, SequelizeMeta)
})
.error(function(err) { console.log(err) })
}.bind(this)
})
describe('getUndoneMigrations', function() {
it("returns no files if timestamps are after the files timestamp", function(done) {
this.init({ from: 20120101010101 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(0)
done()
})
})
})
it("returns only files between from and to", function(done) {
this.init({ from: 19700101000000, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(1)
expect(migrations[migrations.length - 1].filename).to.equal('20111117063700-createPerson.js')
done()
})
})
})
it("returns exactly the migration which is defined in from and to", function(done) {
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations.length).to.equal(1)
expect(migrations[migrations.length - 1].filename).to.equal('20111117063700-createPerson.js')
done()
})
})
})
it("returns also the file which is exactly options.from or options.to", function(done) {
this.init({ from: 20111117063700, to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(2)
expect(migrations[0].filename).to.equal('20111117063700-createPerson.js')
expect(migrations[1].filename).to.equal('20111130161100-emptyMigration.js')
done()
})
})
})
it("returns all files to options.to if no options.from is defined", function(done) {
this.init({ to: 20111130161100 }, function(migrator) {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(2)
done()
})
})
})
it("returns all files from last migration id stored in database", function(done) {
this.init(undefined, function(migrator, SequelizeMeta) {
SequelizeMeta.create({ from: null, to: 20111117063700 }).success(function() {
migrator.getUndoneMigrations(function(err, migrations) {
expect(err).to.be.null
expect(migrations).to.have.length(6)
expect(migrations[0].filename).to.equal('20111130161100-emptyMigration.js')
done()
})
})
})
})
})
describe('migrations', function() {
beforeEach(function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
self.migrator = migrator
self.migrator.migrate().success(done)
})
})
describe('executions', function() {
it("executes migration #20111117063700 and correctly creates the table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'Person' ])
done()
})
})
it("executes migration #20111117063700 and correctly adds isBetaMember", function(done) {
this.sequelize.getQueryInterface().describeTable('Person').success(function(data) {
var fields = Object.keys(data).sort()
expect(fields).to.eql([ 'isBetaMember', 'name' ])
done()
})
})
it("executes migration #20111117063700 correctly up (createTable) and downwards (dropTable)", function(done) {
var self = this
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'Person' ])
self.migrator.migrate({ method: 'down' }).success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([])
done()
})
})
})
})
it("executes the empty migration #20111130161100", function(done) {
this.init({ from: 20111130161100, to: 20111130161100 }, function(migrator) {
// this migration isn't actually testing anything but
// should not timeout
// expect(1).to.equal(1)
migrator
.migrate()
.success(done)
.error(function(err) { console.log(err) })
})
})
})
describe('renameTable', function() {
beforeEach(function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111117063700 }, function(migrator) {
self.migrator = migrator
self.migrator.migrate().success(done)
})
})
it("executes migration #20111205064000 and renames a table", function(done) {
var self = this
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e) { return e != 'SequelizeMeta' })
expect(tableNames).to.include('Person')
self.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).to.eql([ 'User' ])
done()
})
})
})
})
})
})
describe('addColumn', function() {
it('adds a column to the user table', function(done) {
var self = this
this.init({ from: 20111117063700, to: 20111205162700 }, function(migrator) {
migrator.migrate().complete(function(err) {
self.sequelize.getQueryInterface().describeTable('User').complete(function(err, data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).to.be.true
expect(isAdmin.allowNull).to.be.false
if (dialect === "postgres" || dialect === "postgres-native" || dialect === "sqlite") {
expect(isAdmin.defaultValue).to.be.false
} else {
expect(isAdmin.defaultValue).to.equal("0")
}
expect(shopId.allowNull).to.be.true
done()
})
})
})
})
})
describe('removeColumn', function() {
it('removes the shopId column from user', function(done) {
var self = this
this.init({ to: 20111206061400 }, function(migrator) {
migrator.migrate().success(function(){
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, isAdmin = data.isAdmin
, shopId = data.shopId
expect(signature.allowNull).to.be.true
expect(isAdmin.allowNull).to.be.false
if (dialect === "postgres" || dialect === "postgres-native" || dialect === "sqlite") {
expect(isAdmin.defaultValue).to.be.false
} else {
expect(isAdmin.defaultValue).to.equal("0")
}
expect(shopId).to.be.not.ok
done()
})
})
})
})
})
describe('changeColumn', function() {
it('changes the signature column from user to default "signature" + notNull', function(done) {
var self = this
this.init({ to: 20111206063000 }, function(migrator) {
migrator.migrate().success(function() {
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
if (dialect === 'postgres') {
expect(signature.type).to.equal('CHARACTER VARYING')
} else {
expect(signature.type).to.equal('VARCHAR(255)')
}
expect(signature.allowNull).to.equal(false)
expect(signature.defaultValue).to.equal('Signature')
done()
})
})
})
})
})
})
describe('renameColumn', function() {
it("renames the signature column from user to sig", function(done) {
var self = this
this.init({ to: 20111206163300 }, function(migrator) {
migrator.migrate().success(function(){
self.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature
, sig = data.sig
expect(signature).to.not.be.ok
expect(sig).to.be.ok
done()
})
})
})
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe('[MYSQL Specific] Associations', function() {
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: DataTypes.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING})
, self = this
Table1.hasMany(Table2)
Table2.hasMany(Table1)
Table1.sync({ force: true }).success(function() {
Table2.sync({ force: true }).success(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist
done()
})
})
})
})
describe('when join table name is specified', function() {
beforeEach(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.STRING})
Table1.hasMany(Table2, {joinTableName: 'table1_to_table2'})
Table2.hasMany(Table1, {joinTableName: 'table1_to_table2'})
Table1.sync({ force: true }).success(function() {
Table2.sync({ force: true }).success(function() {
done()
})
})
})
it("should not use only a specified name", function() {
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.to.exist
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
})
})
})
describe('HasMany', function() {
beforeEach(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: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: DataTypes.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.User.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(function() {
done()
})
})
})
})
})
describe('addDAO / getDAO', function() {
beforeEach(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).to.equal(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).to.equal(1)
done()
})
})
})
})
})
describe('removeDAO', function() {
beforeEach(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).to.equal(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).to.equal(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).to.equal(self.tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
}
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe('[MYSQL Specific] Connector Manager', function() {
this.timeout(10000)
it('works correctly after being idle', function(done) {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
, spy = sinon.spy()
User.sync({force: true}).on('success', function() {
User.create({username: 'user1'}).on('success', function() {
User.count().on('success', function(count) {
expect(count).to.equal(1)
spy()
setTimeout(function() {
User.count().on('success', function(count) {
expect(count).to.equal(1)
spy()
if (spy.calledTwice) {
done()
}
})
}, 1000)
})
})
})
})
})
}
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe("[MYSQL Specific] DAOFactory", function () {
describe('constructor', function() {
it("handles extended attributes (unique)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: { type: DataTypes.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (default)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (null)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (comment)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (primaryKey)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) PRIMARY KEY"})
done()
})
it("adds timestamps", function(done) {
var User1 = this.sequelize.define('User' + config.rand(), {})
var User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true })
expect(User1.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
expect(User2.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("adds deletedAt if paranoid", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("underscores timestamps if underscored", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
done()
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
foo: {type: DataTypes.STRING, primaryKey: true},
bar: DataTypes.STRING
})
expect(User.primaryKeys).to.deep.equal({"foo":"VARCHAR(255) PRIMARY KEY"})
done()
})
})
})
}
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)
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, util = require("util")
, _ = require('lodash')
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
describe('QueryGenerator', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe("[MYSQL Specific] QueryGenerator", function () {
var suites = {
attributesToSQL: [
{
arguments: [{id: 'INTEGER'}],
......@@ -178,9 +179,9 @@ describe('QueryGenerator', function() {
expectation: "SELECT * FROM `myTable` LIMIT 2, 10;",
context: QueryGenerator
}, {
title: 'ignores offset if no limit was passed',
title: 'uses default limit if only offset is specified',
arguments: ['myTable', {offset: 2}],
expectation: "SELECT * FROM `myTable`;",
expectation: "SELECT * FROM `myTable` LIMIT 2, 18440000000000000000;",
context: QueryGenerator
}, {
title: 'multiple where arguments',
......@@ -235,10 +236,10 @@ describe('QueryGenerator', function() {
context: {options: {omitNull: true}}
}, {
arguments: ['myTable', {foo: false}],
expectation: "INSERT INTO `myTable` (`foo`) VALUES (0);"
expectation: "INSERT INTO `myTable` (`foo`) VALUES (false);"
}, {
arguments: ['myTable', {foo: true}],
expectation: "INSERT INTO `myTable` (`foo`) VALUES (1);"
expectation: "INSERT INTO `myTable` (`foo`) VALUES (true);"
}
],
......@@ -272,7 +273,7 @@ describe('QueryGenerator', function() {
context: {options: {omitNull: true}} // Note: As above
}, {
arguments: ['myTable', [{name: "foo", value: true}, {name: 'bar', value: false}]],
expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',1),('bar',0);"
expectation: "INSERT INTO `myTable` (`name`,`value`) VALUES ('foo',true),('bar',false);"
}
],
......@@ -302,10 +303,10 @@ describe('QueryGenerator', function() {
context: {options: {omitNull: true}}
}, {
arguments: ['myTable', {bar: false}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=0 WHERE `name`='foo'"
expectation: "UPDATE `myTable` SET `bar`=false WHERE `name`='foo'"
}, {
arguments: ['myTable', {bar: true}, {name: 'foo'}],
expectation: "UPDATE `myTable` SET `bar`=1 WHERE `name`='foo'"
expectation: "UPDATE `myTable` SET `bar`=true WHERE `name`='foo'"
}
],
......@@ -385,11 +386,11 @@ describe('QueryGenerator', function() {
},
{
arguments: [{ maple: false, bacon: true }],
expectation: "`maple`=0 AND `bacon`=1"
expectation: "`maple`=false AND `bacon`=true"
},
{
arguments: [{ beaver: [false, true] }],
expectation: "`beaver` IN (0,1)"
expectation: "`beaver` IN (false,true)"
},
{
arguments: [{birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55))}],
......@@ -407,19 +408,20 @@ describe('QueryGenerator', function() {
]
}
Sequelize.Utils._.each(suites, function(tests, suiteTitle) {
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
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
var context = test.context || {options: {}};
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
})
}
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, config = require(__dirname + '/../config/config')
, DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] associations', function() {
beforeEach(function(done) {
this.sequelize.options.quoteIdentifier = true
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: DataTypes.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: DataTypes.STRING})
Table1.hasMany(Table2)
Table2.hasMany(Table1)
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist
done()
})
})
describe('when join table name is specified', function() {
beforeEach(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: DataTypes.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: DataTypes.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.to.exist
done()
})
it("should use the specified name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
done()
})
})
})
describe('HasMany', function() {
describe('addDAO / getDAO', function() {
beforeEach(function(done) {
var self = this
//prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + config.rand(), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + config.rand(), { name: DataTypes.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
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()}
}
self.sequelize.getQueryInterface().dropAllTables().success(function() {
self.User.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(function() {
self.User.all().success(function(_users) {
self.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).to.have.length(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(1)
done()
})
})
})
})
})
describe('removeDAO', function() {
it("should correctly remove associated objects", function(done) {
var self = this
, users = []
, tasks = []
//prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + config.rand(), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + config.rand(), { name: DataTypes.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
for (var i = 0; i < 5; ++i) {
users[users.length] = {id: i, name: 'User' + Math.random()}
}
for (var x = 0; x < 5; ++x) {
tasks[tasks.length] = {id: i, name: 'Task' + Math.random()}
}
self.User.sync({ force: true }).success(function() {
self.Task.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(function() {
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.task = _tasks[0]
self.users = _users
self.tasks = _tasks
self.user.getTasks().on('success', function(__tasks) {
expect(__tasks).to.have.length(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks).to.have.length(self.tasks.length - 1)
done()
})
})
})
})
})
})
})
})
})
})
})
})
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, config = require(__dirname + '/../config/config')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose()
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] DAO', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
describe('[POSTGRES Specific] DAO', function() {
beforeEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
this.User = this.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)
}
this.User.sync({ force: true }).success(function() {
done()
})
})
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
done()
})
describe('model', function() {
it("create handles array correctly", function(done) {
var self = this
this.User
.create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] })
.success(function(oldUser) {
expect(oldUser.email).toEqual(['foo@bar.com', 'bar@baz.com'])
expect(oldUser.email).to.contain.members(['foo@bar.com', 'bar@baz.com'])
done()
})
.error(function(err) {
......@@ -49,15 +45,15 @@ if (dialect.match(/^postgres/)) {
this.User
.create({ username: 'user', email: ['foo@bar.com'], document: { created: { test: '"value"' }}})
.success(function(newUser) {
expect(newUser.document).toEqual({ created: { test: '"value"' }})
expect(newUser.document).to.deep.equal({ created: { test: '"value"' }})
// Check to see if updating an hstore field works
newUser.updateAttributes({document: {should: 'update', to: 'this', first: 'place'}}).success(function(oldUser){
// Postgres always returns keys in alphabetical order (ascending)
expect(oldUser.document).toEqual({first: 'place', should: 'update', to: 'this'})
expect(oldUser.document).to.deep.equal({first: 'place', should: 'update', to: 'this'})
// Check to see if the default value for an hstore field works
self.User.create({ username: 'user2', email: ['bar@baz.com']}).success(function(defaultUser){
expect(defaultUser.document).toEqual({default: 'value'})
expect(defaultUser.document).to.deep.equal({default: 'value'})
done()
})
})
......@@ -65,60 +61,46 @@ if (dialect.match(/^postgres/)) {
.error(console.log)
})
})
})
describe('[POSTGRES] Unquoted identifiers', function() {
before(function(done) {
it("can insert and select", function(done) {
var self = this
this.sequelize.options.quoteIdentifiers = false
this.sequelize.getQueryInterface().QueryGenerator.options.quoteIdentifiers = false
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.sequelize.options.quoteIdentifiers = false
self.User = sequelize.define('User', {
this.User = this.sequelize.define('Userxs', {
username: DataTypes.STRING,
fullName: DataTypes.STRING // Note mixed case
}, {
quoteIdentifiers: false
})
},
onComplete: function() {
// We can create a table with non-quoted identifiers
self.User.sync({ force: true }).success(done)
}
})
})
it("can insert and select", function(done) {
var self = this
this.User.sync({ force: true }).success(function() {
self.User
.create({ username: 'user', fullName: "John Smith" })
.success(function(user) {
// We can insert into a table with non-quoted identifiers
expect(user.id).toBeDefined()
expect(user.id).not.toBeNull()
expect(user.username).toEqual('user')
expect(user.fullName).toEqual('John Smith')
expect(user.id).to.exist
expect(user.id).not.to.be.null
expect(user.username).to.equal('user')
expect(user.fullName).to.equal('John Smith')
// We can query by non-quoted identifiers
self.User.find({
where: {fullName: "John Smith"}
})
.success(function(user2) {
self.sequelize.options.quoteIndentifiers = true
self.sequelize.getQueryInterface().QueryGenerator.options.quoteIdentifiers = true
self.sequelize.options.logging = false
// We can map values back to non-quoted identifiers
expect(user2.id).toEqual(user.id)
expect(user2.username).toEqual('user')
expect(user2.fullName).toEqual('John Smith')
done();
expect(user2.id).to.equal(user.id)
expect(user2.username).to.equal('user')
expect(user2.fullName).to.equal('John Smith')
done()
})
.error(function(err) {
console.log(err)
})
})
.error(function(err) {
console.log(err)
})
})
})
......
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, hstore = require(__dirname + '/../../lib/dialects/postgres/hstore')
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] hstore', function() {
describe('stringifyPart', function() {
it("handles undefined values correctly", function(done) {
expect(hstore.stringifyPart(undefined)).to.equal('NULL')
done()
})
it("handles null values correctly", function(done) {
expect(hstore.stringifyPart(null)).to.equal('NULL')
done()
})
it("handles boolean values correctly", function(done) {
expect(hstore.stringifyPart(false)).to.equal('false')
expect(hstore.stringifyPart(true)).to.equal('true')
done()
})
it("handles strings correctly", function(done) {
expect(hstore.stringifyPart('foo')).to.equal('"foo"')
done()
})
it("handles strings with backslashes correctly", function(done) {
expect(hstore.stringifyPart("\\'literally\\'")).to.equal('"\\\\\'literally\\\\\'"')
done()
})
it("handles arrays correctly", function(done) {
expect(hstore.stringifyPart([1,['2'],'"3"'])).to.equal('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
done()
})
it("handles simple objects correctly", function(done) {
expect(hstore.stringifyPart({ test: 'value' })).to.equal('"{\\"test\\":\\"value\\"}"')
done()
})
it("handles nested objects correctly", function(done) {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).to.equal('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
done()
})
it("handles objects correctly", function(done) {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).to.equal('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
done()
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.stringify({ })).to.equal('')
done()
})
it('should handle null values correctly', function(done) {
expect(hstore.stringify({ null: null })).to.equal('"null"=>NULL')
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.stringify({ test: 'value' })).to.equal('"test"=>"value"')
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.stringify({ test: { nested: 'value' } })).to.equal('"test"=>"{\\"nested\\":\\"value\\"}"')
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).to.equal('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).to.equal('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
done()
})
})
describe('parse', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.parse('')).to.deep.equal({ })
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.parse('"test"=>"value"')).to.deep.equal({ test: 'value' })
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).to.deep.equal({ test: { nested: 'value' } })
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).to.deep.equal({ test: [ 1, '2', [ '"3"' ] ] })
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: "1", array: [1,'2'], object: { object: 'value' }})
done()
})
})
})
}
var config = require("../config/config")
, Sequelize = require("../../index")
, sequelize = new Sequelize(config.postgres.database, config.postgres.username, config.postgres.password, {
logging: false,
dialect: 'postgres',
port: config.postgres.port
})
, Helpers = new (require("../config/helpers"))(sequelize)
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, QueryGenerator = require("../../lib/dialects/postgres/query-generator")
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, DataTypes = require(__dirname + "/../../lib/data-types")
, moment = require('moment')
, util = require("util")
, _ = require('lodash')
describe('QueryGenerator', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
chai.Assertion.includeStack = true
var suites = {
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] QueryGenerator', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
})
this.User.sync({ force: true }).success(function() {
done()
})
})
var suites = {
attributesToSQL: [
{
arguments: [{id: 'INTEGER'}],
......@@ -337,7 +347,7 @@ describe('QueryGenerator', function() {
arguments: ['myTable', {name: "foo';DROP TABLE myTable;"}],
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 *;"
}, {
arguments: ['myTable', {name: 'foo', foo: 1}],
......@@ -378,7 +388,7 @@ describe('QueryGenerator', function() {
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;') RETURNING *;",
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 *;",
context: {options: {quoteIdentifiers: false}}
}, {
......@@ -425,7 +435,7 @@ describe('QueryGenerator', function() {
arguments: ['myTable', [{name: "foo';DROP TABLE myTable;"}, {name: 'bar'}]],
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 *;"
}, {
arguments: ['myTable', [{name: 'foo', foo: 1}, {name: 'bar', foo: 2}]],
......@@ -466,7 +476,7 @@ describe('QueryGenerator', function() {
expectation: "INSERT INTO myTable (name) VALUES ('foo'';DROP TABLE myTable;'),('bar') RETURNING *;",
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 *;",
context: {options: {quoteIdentifiers: false}}
}, {
......@@ -506,10 +516,10 @@ describe('QueryGenerator', function() {
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 *"
}, {
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 *"
}, {
arguments: ['myTable', {bar: 2}, {name: 'foo'}],
......@@ -533,7 +543,7 @@ describe('QueryGenerator', function() {
expectation: "UPDATE \"myTable\" SET \"bar\"=2 WHERE \"name\"='foo' RETURNING *",
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 *"
}, {
arguments: ['mySchema.myTable', {name: "foo';DROP TABLE mySchema.myTable;"}, {name: 'foo'}],
......@@ -542,11 +552,11 @@ describe('QueryGenerator', function() {
// 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 *",
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 *",
context: {options: {quoteIdentifiers: false}}
}, {
......@@ -574,7 +584,7 @@ describe('QueryGenerator', function() {
expectation: "UPDATE myTable SET bar=2 WHERE name='foo' RETURNING *",
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 *",
context: {options: {quoteIdentifiers: false}}
}, {
......@@ -758,19 +768,26 @@ describe('QueryGenerator', function() {
]
}
Sequelize.Utils._.each(suites, function(tests, suiteTitle) {
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
QueryGenerator.options.quoteIdentifiers = true
done()
})
tests.forEach(function(test) {
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
var context = test.context || {options: {}};
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
})
}
\ No newline at end of file
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Promise"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1, 20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
this.User.sync({ force: true }).then(function() { done() })
})
describe('increment', function () {
beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
})
it('with array', function(done) {
var self = this
this.User
.find(1)
.then(function(user) {
expect(user.id).to.equal(1)
return user.increment(['aNumber'], 2)
})
.then(function(user) {
// The following assertion would rock hard, but it's not implemented :(
// expect(user.aNumber).to.equal(2)
return self.User.find(1)
})
.then(function(user) {
expect(user.aNumber).to.equal(2)
done()
})
});
it('should still work right with other concurrent updates', function(done) {
var self = this
// Select something
this.User
.find(1)
.then(function (user1) {
// Select the user again (simulating a concurrent query)
return self.User.find(1)
.then(function (user2) {
return user2
.updateAttributes({ aNumber: user2.aNumber + 1 })
.then(function() { return user1.increment(['aNumber'], 2) })
.then(function() { return self.User.find(1) })
.then(function(user5) {
expect(user5.aNumber).to.equal(3)
done()
})
})
})
})
it('with key value pair', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.increment({ 'aNumber': 1, 'bNumber': 2})
})
.then(function () {
return self.User.find(1)
})
.then(function (user3) {
expect(user3.aNumber).to.equal(1)
expect(user3.bNumber).to.equal(2)
done()
})
})
})
describe('decrement', function () {
beforeEach(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
})
it('with array', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.decrement(['aNumber'], 2)
})
.then(function(user2) {
return self.User.find(1)
})
.then(function(user3) {
expect(user3.aNumber).to.equal(-2)
done()
})
})
it('with single field', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
return user1.decrement(['aNumber'], 2)
})
.then(function(user3) {
return self.User.find(1)
})
.then(function (user3) {
expect(user3.aNumber).to.equal(-2)
done()
})
})
it('should still work right with other concurrent decrements', function(done) {
var self = this
this.User
.find(1)
.then(function(user1) {
var _done = _.after(3, function() {
self.User
.find(1)
.then(function(user2) {
expect(user2.aNumber).to.equal(-6)
done()
})
})
user1.decrement(['aNumber'], 2).done(_done)
user1.decrement(['aNumber'], 2).done(_done)
user1.decrement(['aNumber'], 2).done(_done)
})
})
})
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function(done) {
this.User
.create({ username: 'John Doe' })
.then(function(originalUser) {
return originalUser
.updateAttributes({ username: 'Doe John' })
.then(function () {
return originalUser.reload()
})
.then(function(updatedUser) {
expect(originalUser === updatedUser).to.be.true
done()
})
})
})
it("should update the values on all references to the DAO", function(done) {
var self = this
this.User
.create({ username: 'John Doe' })
.then(function(originalUser) {
return self.User
.find(originalUser.id)
.then(function(updater) {
return updater.updateAttributes({ username: 'Doe John' })
})
.then(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).to.equal('John Doe')
return originalUser.reload()
}).then(function(updatedUser) {
expect(originalUser.username).to.equal('Doe John')
expect(updatedUser.username).to.equal('Doe John')
done()
})
})
})
it("should update the associations as well", function (done) {
var Book = this.sequelize.define('Book', { title: DataTypes.STRING })
, Page = this.sequelize.define('Page', { content: DataTypes.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
Book
.sync({ force: true })
.then(function() {
Page
.sync({ force: true })
.then(function() {
return Book.create({ title: 'A very old book' })
})
.then(function (book) {
return Page
.create({ content: 'om nom nom' })
.then(function(page) {
return book
.setPages([ page ])
.then(function() {
return Book
.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
})
.then(function (leBook) {
return page
.updateAttributes({ content: 'something totally different' })
.then(function (page) {
expect(leBook.pages[0].content).to.equal('om nom nom')
expect(page.content).to.equal('something totally different')
return leBook
.reload()
.then(function (leBook) {
expect(leBook.pages[0].content).to.equal('something totally different')
expect(page.content).to.equal('something totally different')
done()
})
})
})
})
})
}, done)
})
})
})
describe('complete', function () {
it("gets triggered if an error occurs", function(done) {
this.User.find({ where: "asdasdasd" }).then(null, function(err) {
expect(err).to.be.ok
expect(err.message).to.be.ok
done()
})
})
it("gets triggered if everything was ok", function(done) {
this.User.count().then(function(result) {
expect(result).to.not.be.undefined
done()
})
})
})
describe('save', function () {
it('should fail a validation upon creating', function(done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.greaterThan(-1)
done()
});
})
it('should fail a validation upon building', function(done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateCustom).to.be.ok
expect(err.validateCustom).to.be.an("array")
expect(err.validateCustom[0]).to.be.ok
expect(err.validateCustom[0]).to.equal('Length failed.')
done()
})
})
it('should fail a validation when updating', function(done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function(err) {
expect(err).to.be.ok
expect(err).to.be.an("object")
expect(err.validateTest).to.be.ok
expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.greaterThan(-1)
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
before(function() {
describe(Support.getTestDialectTeaser("QueryChainer"), function () {
beforeEach(function(done) {
this.queryChainer = new QueryChainer()
done()
})
describe('add', function() {
it('adds a new serial item if method is passed', function() {
expect(this.queryChainer.serials.length).toEqual(0)
it('adds a new serial item if method is passed', function(done) {
expect(this.queryChainer.serials.length).to.equal(0)
this.queryChainer.add({}, 'foo')
expect(this.queryChainer.serials.length).toEqual(1)
expect(this.queryChainer.serials.length).to.equal(1)
done()
})
it('adds a new emitter if no method is passed', function() {
expect(this.queryChainer.emitters.length).toEqual(0)
it('adds a new emitter if no method is passed', function(done) {
expect(this.queryChainer.emitters.length).to.equal(0)
this.queryChainer.add(new CustomEventEmitter())
expect(this.queryChainer.emitters.length).toEqual(1)
expect(this.queryChainer.emitters.length).to.equal(1)
done()
})
})
......@@ -37,7 +37,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2)
this.queryChainer.run().success(function() {
assert(true)
expect(true).to.be.true
done()
}).error(function(err) {
console.log(err)
......@@ -53,9 +53,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter1)
this.queryChainer.run().success(function(results) {
expect(results).toBeDefined()
expect(results.length).toEqual(1)
expect(results[0]).toEqual(1)
expect(results).to.exist
expect(results).to.have.length(1)
expect(results[0]).to.equal(1)
done()
})
......@@ -72,8 +72,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter3)
this.queryChainer.run().success(function(results) {
expect(results.length).toEqual(3)
expect(results).toEqual([1,2,3])
expect(results).to.have.length(3)
expect(results).to.include.members([1,2,3])
done()
})
......@@ -90,10 +90,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2)
this.queryChainer.run().success(function(results, result1, result2) {
expect(result1).toBeDefined()
expect(result2).toBeDefined()
expect(result1).toEqual(1)
expect(result2).toEqual(2)
expect(result1).to.exist
expect(result2).to.exist
expect(result1).to.equal(1)
expect(result2).to.equal(2)
done()
})
......@@ -111,7 +111,7 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2, 'run')
this.queryChainer.runSerially().success(function() {
assert(true)
expect(true).to.be.true
done()
})
})
......@@ -122,9 +122,9 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter1, 'run')
this.queryChainer.runSerially().success(function(results) {
expect(results).toBeDefined()
expect(results.length).toEqual(1)
expect(results[0]).toEqual(1)
expect(results).to.exist
expect(results).to.have.length(1)
expect(results[0]).to.equal(1)
done()
})
})
......@@ -139,8 +139,8 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter3, 'run')
this.queryChainer.runSerially().success(function(results) {
expect(results.length).toEqual(3)
expect(results).toEqual([1,2,3])
expect(results).to.have.length(3)
expect(results).to.contain.members([1,2,3])
done()
})
})
......@@ -153,10 +153,10 @@ describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
this.queryChainer.add(emitter2, 'run')
this.queryChainer.runSerially().success(function(results, result1, result2) {
expect(result1).toBeDefined()
expect(result2).toBeDefined()
expect(result1).toEqual(1)
expect(result2).toEqual(2)
expect(result1).to.exist
expect(result2).to.exist
expect(result1).to.equal(1)
expect(result2).to.equal(2)
done()
})
})
......
/* jshint multistr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("QueryGenerators"), function () {
describe("comments", function() {
it("should create a comment for a column", function(done) {
var self = this
, User = this.sequelize.define('User', {
username: {type: DataTypes.STRING, comment: 'Some lovely info for my DBA'}
})
User.sync({ force: true }).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).to.be.true
return done()
} else {
console.log('FIXME: This dialect is not supported :(');
expect(true).to.be.true
return done()
}
self.sequelize.query(sql, null, {raw: true}).success(function(result) {
expect(result[0].cmt).to.equal('Some lovely info for my DBA');
done()
})
})
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("QueryInterface"), function () {
beforeEach(function(done) {
this.sequelize.options.quoteIdenifiers = true
this.queryInterface = this.sequelize.getQueryInterface()
done()
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
var self = this
this.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
self.queryInterface.createTable('table', { name: DataTypes.STRING }).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.have.length(1)
self.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
done()
})
})
})
})
})
})
})
})
describe('indexes', function() {
before(function(done) {
var self = this
this.queryInterface.dropTable('Users').success(function() {
self.queryInterface.createTable('Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN
}).success(function() {
done()
})
})
})
it('adds, reads and removes an index to the table', function(done) {
var self = this
this.queryInterface.addIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
var indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.include('users_username_is_admin')
self.queryInterface.removeIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.be.empty
done()
})
})
})
})
})
})
describe('describeTable', function() {
it('reads the metadata of the table', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN,
enumVals: DataTypes.ENUM('hello', 'world')
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
expect(err).to.be.null
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).to.equal(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).to.be.true
expect(username.defaultValue).to.be.null
expect(isAdmin.type).to.equal(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).to.be.true
expect(isAdmin.defaultValue).to.be.null
if (dialect === "postgres" || dialect === "postgres-native") {
expect(enumVals.special).to.be.instanceof(Array)
expect(enumVals.special).to.have.length(2);
}
done()
})
})
})
})
})
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
, Sequelize = require(__dirname + '/../index')
, config = require(__dirname + "/config/config")
, moment = require('moment')
chai.Assertion.includeStack = true
var qq = function(str) {
if (dialect == 'postgres' || dialect == 'sqlite') {
return '"' + str + '"'
} else if (dialect == 'mysql') {
return '`' + str + '`'
} else {
return str
}
}
describe(Support.getTestDialectTeaser("Sequelize"), function () {
describe('constructor', function() {
it('should pass the global options correctly', function(done) {
var sequelize = Support.createSequelizeInstance({ logging: false, define: { underscored:true } })
, DAO = sequelize.define('dao', {name: DataTypes.STRING})
expect(DAO.options.underscored).to.be.ok
done()
})
it('should correctly set the host and the port', function(done) {
var sequelize = Support.createSequelizeInstance({ host: '127.0.0.1', port: 1234 })
expect(sequelize.config.port).to.equal(1234)
expect(sequelize.config.host).to.equal('127.0.0.1')
done()
})
})
describe('isDefined', function() {
it("returns false if the dao wasn't defined before", function() {
expect(this.sequelize.isDefined('Project')).to.be.false
})
it("returns true if the dao was defined before", function() {
this.sequelize.define('Project', {
name: DataTypes.STRING
})
expect(this.sequelize.isDefined('Project')).to.be.true
})
})
describe('query', function() {
afterEach(function(done) {
this.sequelize.options.quoteIdentifiers = true
done()
})
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING
})
this.insertQuery = "INSERT INTO " + qq(this.User.tableName) + " (username, " + qq("createdAt") + ", " + qq("updatedAt") + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"
this.User.sync({ force: true }).success(function() {
done()
})
})
it('executes a query the internal way', function(done) {
this.sequelize.query(this.insertQuery, null, { raw: true })
.complete(function(err, result) {
expect(err).to.be.null
expect(result).to.be.null
done()
})
})
it('executes a query if only the sql is passed', function(done) {
this.sequelize.query(this.insertQuery)
.complete(function(err, result) {
expect(err).to.be.null
expect(result).to.not.exist
done()
})
})
it('executes select queries correctly', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize
.query("select * from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
it('executes select queries correctly when quoteIdentifiers is false', function(done) {
var self = this
, seq = Object.create(self.sequelize)
seq.options.quoteIdentifiers = false
seq.query(this.insertQuery).success(function() {
seq.query("select * from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
it('executes select query and parses dot notation results', function(done) {
var self = this
self.sequelize.query('DELETE FROM ' + qq(self.User.tableName)).complete(function() {
self.sequelize.query(self.insertQuery).success(function() {
self.sequelize
.query("select username as " + qq("user.username") + " from " + qq(self.User.tableName) + "")
.complete(function(err, users) {
expect(err).to.be.null
expect(users.map(function(u){ return u.user })).to.deep.equal([{'username':'john'}])
done()
})
})
})
})
if (dialect == 'mysql') {
it('executes stored procedures', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize.query('DROP PROCEDURE IF EXISTS foo').success(function() {
self.sequelize.query(
"CREATE PROCEDURE foo()\nSELECT * FROM " + self.User.tableName + ";"
).success(function() {
self.sequelize.query('CALL foo()').success(function(users) {
expect(users.map(function(u){ return u.username })).to.include('john')
done()
})
})
})
})
})
} else {
console.log('FIXME: I want to be supported in this dialect as well :-(')
}
it('uses the passed DAOFactory', function(done) {
var self = this
self.sequelize.query(this.insertQuery).success(function() {
self.sequelize.query("SELECT * FROM " + qq(self.User.tableName) + ";", self.User).success(function(users) {
expect(users[0].__factory).to.equal(self.User)
done()
})
})
})
it('destructs dot separated attributes when doing a raw query', function(done) {
var tickChar = (dialect === 'postgres') ? '"' : '`'
, sql = "select 1 as " + Sequelize.Utils.addTicks('foo.bar.baz', tickChar)
this.sequelize.query(sql, null, { raw: true }).success(function(result) {
expect(result).to.deep.equal([ { foo: { bar: { baz: 1 } } } ])
done()
})
})
it('replaces token with the passed array', function(done) {
this.sequelize.query('select ? as foo, ? as bar', null, { raw: true }, [ 1, 2 ]).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }])
done()
})
})
it('handles AS in conjunction with functions just fine', function(done) {
this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).to.be.true
done()
})
})
})
describe('define', function() {
it("adds a new dao to the dao manager", function(done) {
expect(this.sequelize.daoFactoryManager.all.length).to.equal(0)
this.sequelize.define('foo', { title: DataTypes.STRING })
expect(this.sequelize.daoFactoryManager.all.length).to.equal(1)
done()
})
it("overwrites global options", function(done) {
var sequelize = Support.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: DataTypes.STRING}, {collate: 'utf8_bin'})
expect(DAO.options.collate).to.equal('utf8_bin')
done()
})
it("inherits global collate option", function(done) {
var sequelize = Support.createSequelizeInstance({ define: { collate: 'utf8_general_ci' } })
var DAO = sequelize.define('foo', {bar: DataTypes.STRING})
expect(DAO.options.collate).to.equal('utf8_general_ci')
done()
})
it("inherits global classMethods and instanceMethods", function(done) {
var sequelize = Support.createSequelizeInstance({
define: {
classMethods : { globalClassMethod : function() {} },
instanceMethods : { globalInstanceMethod : function() {} }
}
})
var DAO = sequelize.define('foo', {bar: DataTypes.STRING}, {
classMethods : { localClassMethod : function() {} }
})
expect(typeof DAO.options.classMethods.globalClassMethod).to.equal('function')
expect(typeof DAO.options.classMethods.localClassMethod).to.equal('function')
expect(typeof DAO.options.instanceMethods.globalInstanceMethod).to.equal('function')
done()
})
it("uses the passed tableName", function(done) {
var self = this
, Photo = this.sequelize.define('Foto', { name: DataTypes.STRING }, { tableName: 'photos' })
Photo.sync({ force: true }).success(function() {
self.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
expect(tableNames).to.include('photos')
done()
})
})
})
})
describe('sync', function() {
it("synchronizes all daos", function(done) {
var Project = this.sequelize.define('project' + config.rand(), { title: DataTypes.STRING })
var Task = this.sequelize.define('task' + config.rand(), { title: DataTypes.STRING })
Project.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
Project.create({title: 'bla'}).success(function() {
Task.create({title: 'bla'}).success(function(task){
expect(task).to.exist
expect(task.title).to.equal('bla')
done()
})
})
})
})
})
it('works with correct database credentials', function(done) {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
User.sync().success(function() {
expect(true).to.be.true
done()
})
})
it("fails with incorrect database credentials", function(done) {
// sqlite doesn't have a concept of database credentials
if (dialect === "sqlite") {
expect(true).to.be.true
return done()
}
var sequelize2 = Support.getSequelizeInstance('foo', 'bar', null, { logging: false })
, User2 = sequelize2.define('User', { name: DataTypes.STRING, bio: DataTypes.TEXT })
User2.sync().error(function(err) {
if (dialect === "postgres" || dialect === "postgres-native") {
expect(err.message).to.equal('role "bar" does not exist')
} else {
expect(err.message.toString()).to.match(/.*Access\ denied.*/)
}
done()
})
})
})
describe('drop should work', function() {
it('correctly succeeds', function(done) {
var User = this.sequelize.define('Users', {username: DataTypes.STRING })
User.sync({ force: true }).success(function() {
User.drop().success(function() {
expect(true).to.be.true
done()
})
})
})
})
describe('import', function() {
it("imports a dao definition from a file", function(done) {
var Project = this.sequelize.import(__dirname + "/assets/project")
expect(Project).to.exist
done()
})
})
describe('define', function() {
[
{ type: DataTypes.ENUM, values: ['scheduled', 'active', 'finished']},
DataTypes.ENUM('scheduled', 'active', 'finished')
].forEach(function(status) {
describe('enum', function() {
beforeEach(function(done) {
this.Review = this.sequelize.define('review', { status: status })
this.Review.sync({ force: true }).success(function() {
done()
})
})
it('raises an error if no values are defined', function(done) {
var self = this
expect(function() {
self.sequelize.define('omnomnom', {
bla: { type: DataTypes.ENUM }
})
}).to.throw(Error, 'Values for ENUM haven\'t been defined.')
done()
})
it('correctly stores values', function(done) {
this.Review.create({ status: 'active' }).success(function(review) {
expect(review.status).to.equal('active')
done()
})
})
it('correctly loads values', function(done) {
var self = this
this.Review.create({ status: 'active' }).success(function() {
self.Review.findAll().success(function(reviews) {
expect(reviews[0].status).to.equal('active')
done()
})
})
})
it("doesn't save an instance if value is not in the range of enums", function(done) {
var self = this
expect(function() {
self.Review.create({ status: 'fnord' })
}).to.throw(Error, 'Value "fnord" for ENUM status is out of allowed scope. Allowed values: scheduled, active, finished')
done()
})
})
})
describe('table', function() {
[
{ id: { type: DataTypes.BIGINT } },
{ id: { type: DataTypes.STRING, allowNull: true } },
{ id: { type: DataTypes.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].forEach(function(customAttributes) {
it('should be able to override options on the default attributes', function(done) {
var Picture = this.sequelize.define('picture', _.cloneDeep(customAttributes))
Picture.sync({ force: true }).success(function() {
Object.keys(customAttributes).forEach(function(attribute) {
Object.keys(customAttributes[attribute]).forEach(function(option) {
var optionValue = customAttributes[attribute][option];
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue)
})
})
done()
})
})
})
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, dbFile = __dirname + '/test.sqlite'
, storages = [dbFile]
chai.Assertion.includeStack = true
if (dialect === 'sqlite') {
describe('[SQLITE Specific] DAOFactory', function() {
after(function(done) {
this.sequelize.options.storage = ':memory:'
done()
})
beforeEach(function(done) {
this.sequelize.options.storage = dbFile
this.User = this.sequelize.define('User', {
age: DataTypes.INTEGER,
name: DataTypes.STRING,
bio: DataTypes.TEXT
})
this.User.sync({ force: true }).success(function() {
done()
})
})
storages.forEach(function(storage) {
describe('with storage "' + storage + '"', function() {
after(function(done) {
if (storage === dbFile) {
require("fs").writeFile(dbFile, '', function() {
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).to.equal(21)
expect(user.name).to.equal('John Wayne')
expect(user.bio).to.equal('noot noot')
self.User.all().success(function(users) {
var usernames = users.map(function(user) {
return user.name
})
expect(usernames).to.contain('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).to.deep.equal(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).to.be.ok
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).to.not.be.ok
done()
})
})
})
})
describe('.find', function() {
beforeEach(function(done) {
this.User.create({name: 'user', bio: 'footbar'}).success(function() {
done()
})
})
it("finds normal lookups", function(done) {
this.User.find({ where: { name:'user' } }).success(function(user) {
expect(user.name).to.equal('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).to.equal('user')
done()
})
})
})
describe('.all', function() {
beforeEach(function(done) {
this.User.bulkCreate([
{name: 'user', bio: 'foobar'},
{name: 'user', bio: 'foobar'}
]).success(function() {
done()
})
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users).to.have.length(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).to.equal(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).to.equal(5)
done()
})
})
})
})
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
buster.spec.expose()
chai.Assertion.includeStack = true
if (dialect === 'sqlite') {
describe('[SQLITE] DAO', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
describe('[SQLITE Specific] DAO', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
this.User.sync({ force: true }).success(function() {
done()
})
})
......@@ -32,21 +23,16 @@ if (dialect === 'sqlite') {
this.User
.create({ username: 'user', createdAt: new Date(2011, 04, 04) })
.success(function(oldUser) {
self.User
.create({ username: 'new user' })
.success(function(newUser) {
.success(function() {
self.User.create({ username: 'new user' }).success(function() {
self.User.findAll({
where: ['createdAt > ?', new Date(2012, 01, 01)]
}).success(function(users) {
expect(users.length).toEqual(1)
expect(users).to.have.length(1)
done()
})
})
})
.error(function(err) {
console.log(err)
})
})
})
})
......
var Sequelize = require("../../index")
, sequelize = new Sequelize(null, null, null, { dialect: 'sqlite' })
, Helpers = new (require("../config/helpers"))(sequelize)
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, util = require("util")
, _ = require('lodash')
, QueryGenerator = require("../../lib/dialects/sqlite/query-generator")
, util = require("util");
describe('QueryGenerator', function() {
beforeEach(function() { Helpers.sync() })
afterEach(function() { Helpers.drop() })
chai.Assertion.includeStack = true
var suites = {
if (dialect === 'sqlite') {
describe('[SQLITE Specific] QueryGenerator', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING
})
this.User.sync({ force: true }).success(function() {
done()
})
})
var suites = {
attributesToSQL: [
{
arguments: [{id: 'INTEGER'}],
......@@ -212,20 +223,23 @@ describe('QueryGenerator', function() {
expectation: "DELETE FROM `myTable` WHERE `name`='foo'"
}
]
};
}
Sequelize.Utils._.each(suites, function(tests, suiteTitle) {
_.each(suites, function(tests, suiteTitle) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
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
var context = test.context || {options: {}};
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).toEqual(test.expectation)
expect(conditions).to.deep.equal(test.expectation)
done()
})
})
})
})
})
});
}
File mode changed
const Sequelize = require(__dirname + "/../index")
var fs = require('fs')
, Sequelize = require(__dirname + "/../index")
, DataTypes = require(__dirname + "/../lib/data-types")
, config = require(__dirname + "/config/config")
, fs = require('fs')
, buster = require("buster")
var BusterHelpers = module.exports = {
var Support = {
Sequelize: Sequelize,
initTests: function(options) {
......@@ -30,25 +29,41 @@ var BusterHelpers = module.exports = {
options.dialect = options.dialect || 'mysql'
options.logging = (options.hasOwnProperty('logging') ? options.logging : false)
options.pool = options.pool || config.pool
var sequelizeOptions = {
logging: options.logging,
dialect: options.dialect,
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') {
sequelizeOptions.native = true
}
return new Sequelize(
config[options.dialect].database,
config[options.dialect].username,
config[options.dialect].password,
return this.getSequelizeInstance(
process.env.SEQ_DB || config[options.dialect].database,
process.env.SEQ_USER || process.env.SEQ_USERNAME || config[options.dialect].username,
process.env.SEQ_PW || process.env.SEQ_PASSWORD || config[options.dialect].password,
sequelizeOptions
)
},
getSequelizeInstance: function(db, user, pass, options) {
options = options || {};
options.dialect = options.dialect || this.getTestDialect()
return new Sequelize(db, user, pass, options)
},
clearDatabase: function(sequelize, callback) {
sequelize
.getQueryInterface()
......@@ -66,6 +81,14 @@ var BusterHelpers = module.exports = {
})
},
checkMatchForDialects: function(dialect, value, expectations) {
if (!!expectations[dialect]) {
expect(value).to.match(expectations[dialect])
} else {
throw new Error('Undefined expectation for "' + dialect + '"!')
}
},
getTestDialect: function() {
var envDialect = process.env.DIALECT || 'mysql'
......@@ -88,22 +111,20 @@ var BusterHelpers = module.exports = {
}
return "[" + dialect.toUpperCase() + "] " + moduleName
},
checkMatchForDialects: function(dialect, value, expectations) {
if (!!expectations[dialect]) {
expect(value).toMatch(expectations[dialect])
} else {
throw new Error('Undefined expectation for "' + dialect + '"!')
}
},
assertException: function(block, msg) {
try {
block()
throw new Error('Passed function did not throw an error')
} catch(e) {
buster.assert.equals(e.message, msg)
}
}
}
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
before(function(done) {
this.sequelize = sequelize
done()
})
afterEach(function(done) {
Support.clearDatabase(this.sequelize, function() {
done()
})
})
module.exports = Support
var chai = require('chai')
, expect = chai.expect
, Utils = require(__dirname + '/../lib/utils')
, Support = require(__dirname + '/support')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("Utils"), function() {
describe('removeCommentsFromFunctionString', function() {
it("removes line comments at the start of a line", function(done) {
var functionWithLineComments = function() {
// noot noot
}
var string = functionWithLineComments.toString()
, result = Utils.removeCommentsFromFunctionString(string)
expect(result).not.to.match(/.*noot.*/)
done()
})
it("removes lines comments in the middle of a line", function(done) {
var functionWithLineComments = function() {
alert(1) // noot noot
}
var string = functionWithLineComments.toString()
, result = Utils.removeCommentsFromFunctionString(string)
expect(result).not.to.match(/.*noot.*/)
done()
})
it("removes range comments", function(done) {
var s = function() {
alert(1) /*
noot noot
*/
alert(2) /*
foo
*/
}.toString()
var result = Utils.removeCommentsFromFunctionString(s)
expect(result).not.to.match(/.*noot.*/)
expect(result).not.to.match(/.*foo.*/)
expect(result).to.match(/.*alert\(2\).*/)
done()
})
})
describe('argsArePrimaryKeys', function() {
it("doesn't detect primary keys if primareyKeys and values have different lengths", function(done) {
expect(Utils.argsArePrimaryKeys([1,2,3], [1])).to.be.false
done()
})
it("doesn't detect primary keys if primary keys are hashes or arrays", function(done) {
expect(Utils.argsArePrimaryKeys([[]], [1])).to.be.false
done()
})
it('detects primary keys if length is correct and data types are matching', function(done) {
expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).to.be.true
done()
})
it("detects primary keys if primary keys are dates and lengths are matching", function(done) {
expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).to.be.true
done()
})
})
describe('underscore', function() {
describe('underscoredIf', function() {
it('is defined', function(done) {
expect(Utils._.underscoredIf).to.be.ok
done()
})
it('underscores if second param is true', function(done) {
expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar')
done()
})
it("doesn't underscore if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', false)).to.equal('fooBar')
done()
})
})
describe('camelizeIf', function() {
it('is defined', function(done) {
expect(Utils._.camelizeIf).to.be.ok
done()
})
it('camelizes if second param is true', function(done) {
expect(Utils._.camelizeIf('foo_bar', true)).to.equal('fooBar')
done()
})
it("doesn't camelize if second param is false", function(done) {
expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar')
done()
})
})
})
describe('isHash', function() {
it('doesn\'t match arrays', function(done) {
expect(Utils.isHash([])).to.be.false
done()
})
it('doesn\'t match null', function(done) {
expect(Utils.isHash(null)).to.be.false
done()
})
it('matches plain objects', function(done) {
var values = {
'name': {
'first': 'Foo',
'last': 'Bar'
}
}
expect(Utils.isHash(values)).to.be.true
done()
})
it('matches plain objects with length property/key', function(done) {
var values = {
'name': {
'first': 'Foo',
'last': 'Bar'
},
'length': 1
}
expect(Utils.isHash(values)).to.be.true
done()
})
})
describe('format', function() {
it('should format where clause correctly when the value is truthy', function(done) {
var where = ['foo = ?', 1]
expect(Utils.format(where)).to.equal('foo = 1')
done()
})
it('should format where clause correctly when the value is false', function(done) {
var where = ['foo = ?', 0]
expect(Utils.format(where)).to.equal('foo = 0')
done()
})
})
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!