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

Commit 3ed27be1 by Sascha Depold

Merge branch 'master' into features/first_steps_for_syntax_sugar

Conflicts:
	package.json
	spec/dao-factory.spec.js
2 parents 551c4e7e 95a23b6d
Showing with 1932 additions and 1767 deletions
......@@ -3,7 +3,7 @@ before_script:
- "psql -c 'create database sequelize_test;' -U postgres"
script:
- "npm run test-buster-travis"
- "make test"
notifications:
email:
......@@ -18,4 +18,4 @@ env:
language: node_js
node_js:
- 0.8
\ No newline at end of file
- 0.8
REPORTER ?= dot
TESTS = $(shell find ./test/* -name "*.test.js")
DIALECT ?= mysql
# test commands
teaser:
@echo "" && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo '# Running tests for $(DIALECT) #' && \
node -pe "Array(20 + '$(DIALECT)'.length + 3).join('#')" && \
echo ''
test:
@make teaser && \
./node_modules/mocha/bin/mocha \
--colors \
--reporter $(REPORTER) \
$(TESTS)
sqlite:
@DIALECT=sqlite make test
mysql:
@DIALECT=mysql make test
postgres:
@DIALECT=postgres make test
postgres-native:
@DIALECT=postgres-native make test
# test aliases
pgsql: postgres
postgresn: postgres-native
# test all the dialects \o/
all: sqlite mysql postgres postgres-native
.PHONY: sqlite mysql postgres pgsql postgres-native postgresn all test
\ No newline at end of file
......@@ -139,30 +139,27 @@ $ npm install
### 4. Run the tests ###
Right now, the test base is split into the `spec` folder (which contains the
lovely [BusterJS](http://busterjs.org) tests).
Right now, the test base is split into the `test` folder (which contains the
lovely [Mocha](http://visionmedia.github.io/mocha/) 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:
```console
$ # run all tests at once:
$ npm test
$ # run all of the buster specs (for all dialects):
$ npm run test-buster
$ make all
$ # run the buster specs for mysql:
$ npm run test-buster-mysql
$ make mysql
$ # run the buster specs for sqlite:
$ npm run test-buster-sqlite
$ make sqlite
$ # run the buster specs for postgresql:
$ npm run test-buster-postgres
$ make pgsql
$ # alternatively you can pass database credentials with $variables when testing with buster.js
$ DIALECT=dialect SEQ_DB=database SEQ_USER=user SEQ_PW=password buster-test
$ # alternatively you can pass database credentials with $variables when testing
$ DIALECT=dialect SEQ_DB=database SEQ_USER=user SEQ_PW=password make test
```
### 5. That's all ###
......
......@@ -2,6 +2,8 @@
- [DEPENDENCIES] Upgraded validator for IPv6 support. [#603](https://github.com/sequelize/sequelize/pull/603). thanks to durango
- [DEPENDENCIES] replaced underscore by lodash. [#954](https://github.com/sequelize/sequelize/pull/594). thanks to durango
- [DEPENDENCIES] Upgraded pg to 2.0.0. [#711](https://github.com/sequelize/sequelize/pull/711). thanks to durango
- [DEPENDENCIES] Upgraded command to 2.0.0 and generic-pool to 2.0.4. thanks to durango
- [DEPENDENCIES] No longer require semver. thanks to durango
- [BUG] Fix string escape with postgresql on raw SQL queries. [#586](https://github.com/sequelize/sequelize/pull/586). thanks to zanamixx
- [BUG] "order by" is now after "group by". [#585](https://github.com/sequelize/sequelize/pull/585). thanks to mekanics
- [BUG] Added decimal support for min/max. [#583](https://github.com/sequelize/sequelize/pull/583). thanks to durango
......@@ -21,6 +23,10 @@
- [BUG] bulkCreate would have problems with a disparate field list [#738](https://github.com/sequelize/sequelize/pull/738). thanks to durango
- [BUG] Fixed problems with quoteIdentifiers and {raw: false} option on raw queries [#751](https://github.com/sequelize/sequelize/pull/751). thanks to janmeier
- [BUG] Fixed SQL escaping with sqlite and unified escaping [#700](https://github.com/sequelize/sequelize/pull/700). thanks to PiPeep
- [BUG] Fixed Postgres' pools [ff57af63](https://github.com/sequelize/sequelize/commit/ff57af63c2eb395b4828a5984a22984acdc2a5e1)
- [BUG] Fixed BLOB/TEXT columns having a default value declared in MySQL [#793](https://github.com/sequelize/sequelize/pull/793). thanks to durango
- [BUG] You can now use .find() on any single integer primary key when throwing just a number as an argument [#796](https://github.com/sequelize/sequelize/pull/796). thanks to durango
- [BUG] Adding unique to a column for Postgres in the migrator should be fixed [#795](https://github.com/sequelize/sequelize/pull/795). thanks to durango
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] 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
......@@ -42,8 +48,12 @@
- [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango
- [FEATURE] Added support for model instances being referenced [#761](https://github.com/sequelize/sequelize/pull/761) thanks to sdepold
- [FEATURE] Added support for specifying the path to load a module for a dialect. [#766](https://github.com/sequelize/sequelize/pull/766) thanks to sonnym.
- [FEATURE] Drop index if exists has been added to sqlite [#766](https://github.com/sequelize/sequelize/pull/776) thanks to coderbuzz
- [FEATURE] bulkCreate() now has a third argument which gives you the ability to validate each row before attempting to bulkInsert [#797](https://github.com/sequelize/sequelize/pull/797). thanks to durango
- [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
- [REFACTORING] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
# v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
......@@ -43,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
})
......@@ -55,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 }))
}
......
......@@ -70,27 +70,35 @@ 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()
}
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
.error(function(err) { emitter.emit('error', err) })
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 {
emitter.emit('success', null)
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)
}
}
})
}).run()
}
......
......@@ -277,14 +277,20 @@ module.exports = (function() {
}
var primaryKeys = this.primaryKeys
, keys = Object.keys(primaryKeys)
, keysLength = keys.length
// options is not a hash but an id
if (typeof options === 'number') {
options = { where: options }
var oldOption = options
options = { where: {} }
if (keysLength === 1) {
options.where[keys[0]] = oldOption
} else {
options.where.id = oldOption
}
} else if (Utils._.size(primaryKeys) && Utils.argsArePrimaryKeys(arguments, primaryKeys)) {
var where = {}
, self = this
, keys = Object.keys(primaryKeys)
Utils._.each(arguments, function(arg, i) {
var key = keys[i]
......@@ -315,7 +321,6 @@ module.exports = (function() {
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;
......@@ -404,7 +409,7 @@ module.exports = (function() {
}
DAOFactory.prototype.build = function(values, options) {
options = options || { isNewRecord: true }
options = options || { isNewRecord: true, isDirty: true }
var self = this
, instance = new this.DAO(values, this.options, options.isNewRecord)
......@@ -412,6 +417,7 @@ module.exports = (function() {
instance.isNewRecord = options.isNewRecord
instance.daoFactoryName = this.name
instance.daoFactory = this
instance.isDirty = options.isDirty
return instance
}
......@@ -461,13 +467,31 @@ module.exports = (function() {
* generated IDs and other default values in a way that can be mapped to
* multiple records
*/
DAOFactory.prototype.bulkCreate = function(records, fields) {
DAOFactory.prototype.bulkCreate = function(records, fields, options) {
options = options || {}
options.validate = options.validate || false
fields = fields || []
var self = this
, daos = records.map(function(v) { return self.build(v) })
, updatedAtAttr = self.options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = self.options.underscored ? 'created_at' : 'createdAt'
fields = fields || []
, errors = []
, daos = records.map(function(v) {
var build = self.build(v)
if (options.validate === true) {
var valid = build.validate({skip: fields})
if (valid !== null) {
errors[errors.length] = {record: v, errors: valid}
}
}
return build
})
if (options.validate === true && errors.length > 0) {
return new Utils.CustomEventEmitter(function(emitter) {
emitter.emit('error', errors)
}).run()
}
// we will re-create from DAOs, which may have set up default attributes
records = []
......@@ -485,7 +509,7 @@ module.exports = (function() {
values[updatedAtAttr] = Utils.now()
}
records.push(values);
records.push(values)
})
// Validate enums
......
var Validator = require("validator")
, Utils = require("./utils")
var DaoValidator = module.exports = function(model) {
var DaoValidator = module.exports = function(model, options) {
options = options || {}
options.skip = options.skip || []
this.model = model
this.options = options
}
DaoValidator.prototype.validate = function() {
......@@ -32,17 +36,19 @@ var validateModel = function() {
}
var validateAttributes = function() {
var errors = {}
var self = this
, errors = {}
// for each field and value
Utils._.each(this.model.dataValues, function(value, field) {
var rawAttribute = this.model.rawAttributes[field]
var rawAttribute = self.model.rawAttributes[field]
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1
if (this.model.validators.hasOwnProperty(field) && !hasAllowedNull) {
errors = Utils._.merge(errors, validateAttribute.call(this, value, field))
if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) {
errors = Utils._.merge(errors, validateAttribute.call(self, value, field))
}
}.bind(this)) // for each field
})
return errors
}
......
......@@ -84,10 +84,15 @@ module.exports = (function() {
DAO.prototype.getDataValue = function(name) {
return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name]
}
DAO.prototype.get = DAO.prototype.getDataValue
DAO.prototype.setDataValue = function(name, value) {
if (Utils.hasChanged(this.dataValues[name], value)) {
this.isDirty = true
}
this.dataValues[name] = value
}
DAO.prototype.set = DAO.prototype.setDataValue
// if an array with field names is passed to save()
// only those fields will be updated
......@@ -120,10 +125,24 @@ module.exports = (function() {
for (var attrName in this.daoFactory.rawAttributes) {
if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) {
var definition = this.daoFactory.rawAttributes[attrName]
, isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString()))
, isHstore = (!!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type)
, hasValue = (typeof values[attrName] !== 'undefined')
, valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
, isEnum = definition.type && (definition.type.toString() === DataTypes.ENUM.toString())
, isHstore = !!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type
, hasValue = values[attrName] !== undefined
, isMySQL = this.daoFactory.daoFactoryManager.sequelize.options.dialect === "mysql"
, ciCollation = !!this.daoFactory.options.collate && this.daoFactory.options.collate.match(/_ci$/i)
, valueOutOfScope
if (isEnum && isMySQL && ciCollation && hasValue) {
var scopeIndex = (definition.values || []).map(function(d) { return d.toLowerCase() }).indexOf(values[attrName].toLowerCase())
valueOutOfScope = scopeIndex === -1
// We'll return what the actual case will be, since a simple SELECT query would do the same...
if (!valueOutOfScope) {
values[attrName] = definition.values[scopeIndex]
}
} else {
valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
}
if (isEnum && hasValue && valueOutOfScope) {
throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', '))
......@@ -149,6 +168,7 @@ module.exports = (function() {
}).run()
}
else if (this.isNewRecord) {
this.isDirty = false
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
} else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id };
......@@ -157,6 +177,7 @@ module.exports = (function() {
identifier = this.__options.whereCollection;
}
this.isDirty = false
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier)
......@@ -191,6 +212,7 @@ module.exports = (function() {
this[valueName] = obj.values[valueName]
}
}
this.isDirty = false
emitter.emit('success', this)
}.bind(this))
}.bind(this)).run()
......@@ -201,8 +223,8 @@ module.exports = (function() {
*
* @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/
DAO.prototype.validate = function() {
var validator = new DaoValidator(this)
DAO.prototype.validate = function(object) {
var validator = new DaoValidator(this, object)
, errors = validator.validate()
return (Utils._.isEmpty(errors) ? null : errors)
......@@ -224,14 +246,21 @@ module.exports = (function() {
readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt')
var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) {
var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1)
)
updateAllowed && (self[attr] = value)
if (updateAllowed) {
if (Utils.hasChanged(self[attr], value)) {
isDirty = true
}
self[attr] = value
}
})
this.isDirty = isDirty
}
DAO.prototype.destroy = function() {
......@@ -330,7 +359,19 @@ module.exports = (function() {
// (the same is true for __defineSetter and 'prototype' getters)
if (has !== true) {
this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; });
this.__defineSetter__(attribute, has.set || function(v) { this.dataValues[attribute] = v; });
this.__defineSetter__(attribute, has.set || function(v) {
if (Utils.hasChanged(this.dataValues[attribute], v)) {
//Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated
var updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt'
, touchedAtAttr = this.__options.underscored ? 'touched_at' : 'touchedAt'
if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) {
this.isDirty = true
}
}
this.dataValues[attribute] = v
});
}
this[attribute] = value;
......
......@@ -262,7 +262,7 @@ module.exports = (function() {
result = transformRowsWithEagerLoadingIntoDaos.call(this, results)
} else {
result = results.map(function(result) {
return this.callee.build(result, { isNewRecord: false })
return this.callee.build(result, { isNewRecord: false, isDirty: false })
}.bind(this))
}
......@@ -287,7 +287,7 @@ module.exports = (function() {
var transformRowWithEagerLoadingIntoDao = function(result, dao) {
// let's build the actual dao instance first...
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false })
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false, isDirty: false })
// ... and afterwards the prefetched associations
for (var tableName in result) {
......@@ -323,7 +323,7 @@ module.exports = (function() {
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
associationData.forEach(function(data) {
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false })
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false, isDirty: false })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
......
......@@ -82,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) {
......@@ -168,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);
}
}
......@@ -180,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;
......@@ -209,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
};
......@@ -265,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
}
}
......@@ -295,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) {
......@@ -342,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)
......@@ -383,4 +386,4 @@ module.exports = (function() {
}
return ConnectorManager
})()
})()
\ No newline at end of file
......@@ -29,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() {
......@@ -377,7 +376,7 @@ module.exports = (function() {
}, options || {})
return Utils._.compact([
"CREATE", options.indicesType, "INDEX IF NOT EXISTS", options.indexName,
"CREATE", options.indicesType, "INDEX", options.indexName,
(options.indexType ? ('USING ' + options.indexType) : undefined),
"ON", tableName, '(' + transformedAttributes.join(', ') + ')',
(options.parser ? "WITH PARSER " + options.parser : undefined)
......@@ -521,7 +520,8 @@ module.exports = (function() {
template += " auto_increment"
}
if ((dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) {
// Blobs/texts cannot have a defaultValue
if (dataType.type !== "TEXT" && dataType.type._binary !== true && (dataType.defaultValue !== undefined) && (dataType.defaultValue != DataTypes.NOW)) {
template += " DEFAULT " + this.escape(dataType.defaultValue)
}
......
......@@ -6,11 +6,12 @@ module.exports = (function() {
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(pgModule).native : require(pgModule)
this.client = null
this.config = config || {}
this.config.port = this.config.port || 5432
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
......@@ -18,43 +19,62 @@ module.exports = (function() {
// 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)
}
// we really want pendingQueries to increment as fast as possible...
self.pendingQueries++
return new Utils.CustomEventEmitter(function(emitter) {
self.connect()
.on('error', function(err) {
// zero-out the previous increment
self.pendingQueries--
self.endQuery.call(self)
emitter.emit('error', err)
})
.on('success', function(done) {
var query = new Query(self.client, self.sequelize, callee, options || {})
done = done || null
query.run(sql, done)
.success(function(results) {
self.pendingQueries--
emitter.emit('success', results)
self.endQuery.call(self)
})
.error(function(err) {
self.pendingQueries--
emitter.emit('error', err)
self.endQuery.call(self)
})
.on('sql', function(sql) { emitter.emit('sql', sql) })
})
}).run()
}
ConnectorManager.prototype.connect = function() {
......@@ -63,7 +83,8 @@ module.exports = (function() {
// in case database is slow to connect, prevent orphaning the client
if (this.isConnecting) {
return
emitter.emit('success')
return emitter
}
this.isConnecting = true
......@@ -71,48 +92,66 @@ 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) {
switch(err.code) {
case 'ECONNREFUSED':
emitter.emit('error', new Error("Failed to authenticate for PostgresSQL. Please double check your settings."))
break
case 'ENOTFOUND':
case 'EHOSTUNREACH':
case 'EINVAL':
emitter.emit('error', new Error("Failed to find PostgresSQL server. Please double check your settings."))
break
default:
emitter.emit('error', 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."))
break
case 'ENOTFOUND':
case 'EHOSTUNREACH':
case 'EINVAL':
emitter.emit('error', new Error("Failed to find PostgresSQL server. Please double check your settings."))
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)
this.client.connect(connectCallback)
if (this.client !== null) {
connectCallback(null, this.client)
} else {
//create one-off client
this.client = new this.pg.Client(uri)
this.client.connect(connectCallback)
}
}
return emitter
}
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
......
......@@ -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)
})
......@@ -189,6 +189,15 @@ module.exports = (function() {
definition = definition.replace(/^ENUM\(.+\)/, this.quoteIdentifier("enum_" + tableName + "_" + attributeName))
}
if (definition.match(/UNIQUE;*$/)) {
definition = definition.replace(/UNIQUE;*$/, '')
attrSql += Utils._.template(query.replace('ALTER COLUMN', ''))({
tableName: this.quoteIdentifiers(tableName),
query: 'ADD CONSTRAINT ' + this.quoteIdentifier(attributeName + '_unique_idx') + ' UNIQUE (' + this.quoteIdentifier(attributeName) + ')'
})
}
attrSql += Utils._.template(query)({
tableName: this.quoteIdentifiers(tableName),
query: this.quoteIdentifier(attributeName) + ' TYPE ' + definition
......
......@@ -18,16 +18,17 @@ 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)
}
var receivedError = false
, query = this.client.query(sql)
, rows = []
var receivedError = false
, query = this.client.query(sql)
, rows = []
query.on('row', function(row) {
rows.push(row)
......@@ -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) {
......@@ -70,7 +72,7 @@ module.exports = (function() {
}
})
} else {
results = rows.map(function(row) { return Utils._.values(row) })
results = rows.map(function(row) { return Utils._.values(row) })
}
return this.emit('success', results)
}
......
......@@ -22,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.')
}
}
......
......@@ -37,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() {
......@@ -89,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)
......@@ -134,6 +143,7 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
selectQuery: function(tableName, options) {
var table = null,
joinQuery = ""
......
......@@ -54,6 +54,12 @@ module.exports = (function() {
return this
}
CustomEventEmitter.prototype.sql =
function(fct) {
this.on('sql', bindToProcess(fct))
return this;
}
CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) {
this.on(eventKey, function (result) {
......
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') {
......@@ -188,7 +188,7 @@ module.exports = (function() {
var self = this;
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer;
var chainer = new Utils.QueryChainer()
var addMigration = function(filename) {
self.options.logging('Adding migration script at ' + filename)
var migration = new Migration(self, filename)
......
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(){}
......
......@@ -88,9 +88,12 @@ var Utils = module.exports = {
type = typeof where[i]
_where[i] = _where[i] || {}
if (Array.isArray(where[i])) {
if (where[i] === null) {
// skip nulls
}
else if (Array.isArray(where[i])) {
_where[i].in = _where[i].in || []
_where[i].in.concat(where[i]);
_where[i].in.concat(where[i])
}
else if (type === "object") {
Object.keys(where[i]).forEach(function(ii) {
......@@ -249,6 +252,23 @@ var Utils = module.exports = {
isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj);
},
hasChanged: function(attrValue, value) {
//If attribute value is Date, check value as a date
if (Utils._.isDate(attrValue) && !Utils._.isDate(value)) {
value = new Date(value)
}
if (Utils._.isDate(attrValue)) {
return attrValue.valueOf() !== value.valueOf()
}
//If both of them are empty, don't set as changed
if ((attrValue === undefined || attrValue === null || attrValue === '') && (value === undefined || value === null || value === '')) {
return false
}
return attrValue !== value
},
argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length == Object.keys(primaryKeys).length)
if (result) {
......@@ -380,6 +400,6 @@ var Utils = module.exports = {
}
}
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": {
......@@ -37,21 +41,24 @@
"lingo": "~0.0.5",
"validator": "~1.3.0",
"moment": "~2.1.0",
"commander": "~1.3.0",
"commander": "~2.0.0",
"dottie": "0.0.8-0",
"toposort-class": "~0.2.0",
"generic-pool": "2.0.3",
"generic-pool": "2.0.4",
"promise": "~3.2.0",
"sql": "~0.26.0"
},
"devDependencies": {
"sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha8",
"pg": "~2.1.0",
"pg": "~2.2.0",
"buster": "~0.6.3",
"watchr": "~2.4.3",
"yuidocjs": "~0.3.36",
"semver": "~2.0.8"
"chai": "~1.7.2",
"mocha": "~1.12.0",
"chai-datetime": "~1.0.0",
"sinon": "~1.7.3"
},
"keywords": [
"mysql",
......@@ -61,13 +68,7 @@
],
"main": "index",
"scripts": {
"test": "npm run test-buster",
"test-buster": "npm run test-buster-mysql && npm run test-buster-postgres && npm run test-buster-postgres-native && npm run test-buster-sqlite",
"test-buster-travis": "node_modules/.bin/buster-test",
"test-buster-mysql": "DIALECT=mysql node_modules/.bin/buster-test",
"test-buster-postgres": "DIALECT=postgres node_modules/.bin/buster-test",
"test-buster-postgres-native": "DIALECT=postgres-native node_modules/.bin/buster-test",
"test-buster-sqlite": "DIALECT=sqlite node_modules/.bin/buster-test",
"test": "make all",
"docs": "node_modules/.bin/yuidoc . -o docs"
},
"bin": {
......
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, Sequelize = require('../../index')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Mixin"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: done
})
})
describe('Mixin', function() {
var DAOFactory = require("../../lib/dao-factory")
it("adds the mixed-in functions to the dao", function() {
expect(DAOFactory.prototype.hasOne).toBeDefined()
expect(DAOFactory.prototype.hasMany).toBeDefined()
expect(DAOFactory.prototype.belongsTo).toBeDefined()
})
})
describe('getAssociation', function() {
it('returns the respective part of the association for 1:1 associations', function() {
var User = this.sequelize.define('User', {})
var Task = this.sequelize.define('Task', {})
User.hasOne(Task)
Task.belongsTo(User)
expect(User.getAssociation(Task).target).toEqual(Task)
})
it('can handle multiple associations just fine', function(done) {
var Emit = require('events').EventEmitter
, emit = new Emit()
, User = this.sequelize.define('User', { username: Sequelize.STRING })
, Event = this.sequelize.define('Event', { name: Sequelize.STRING })
, Place = this.sequelize.define('Place', { name: Sequelize.STRING })
, theSQL = ''
, count = 0
, theEvent
Event.belongsTo(User)
Event.hasOne(Place)
emit.on('sql', function(sql){
++count
theSQL = sql
if (count > 1) {
emit.emit('spy')
}
})
emit.on('find', function(e) {
++count
theEvent = e
if (count > 1) {
emit.emit('spy')
}
})
emit.on('spy', function(){
expect(theSQL.match(/WHERE ["`]Events["`].["`]id["`]=1;$/)).not.toBeNull()
expect(theEvent.name).toEqual('Bob Marley Concert')
expect(theEvent.place.name).toEqual('Backyard')
expect(theEvent.user.username).toEqual('Dan')
done()
})
this.sequelize.sync({force: true }).success(function() {
User.create({username: 'Dan'}).success(function(user){
Event.create({name: 'Bob Marley Concert'}).success(function(event) {
Place.create({name: 'Backyard'}).success(function(place) {
event.setUser(user).success(function(){
event.setPlace(place).success(function(){
Event.find({where: {id: 1}, include: [User, Place]}).success(function(theEvent){
emit.emit('find', theEvent)
})
.on('sql', function(sql){
emit.emit('sql', sql)
})
})
})
})
})
})
})
})
})
})
var config = module.exports
config["node tests"] = {
environment: "node",
rootPath: "../",
tests: [
"spec/**/*.spec.js"
]
}
var Factories = module.exports = function(helpers) {
this.helpers = helpers
this.sequelize = this.helpers.sequelize
}
Factories.prototype.DAO = function(daoName, options, callback, count) {
count = count || 1
var self = this
, daos = []
this.helpers.async(function(done) {
var DAO = self.sequelize.daoFactoryManager.getDAO(daoName)
var create = function(cb) {
DAO.create(options).on('success', function(dao) {
daos.push(dao)
cb && cb()
}).on('error', function(err) {
console.log(err)
done()
})
}
var cb = function() {
if(--count) {
create(cb)
} else {
done()
callback && callback(daos)
}
}
create(cb)
})
}
Factories.prototype.User = function(options, callback, count) {
this.DAO('User', options, callback, count)
}
Sequelize = require("../../index")
var Helpers = module.exports = function(sequelize) {
this.sequelize = sequelize
this.Factories = new (require("./factories"))(this)
}
Helpers.prototype.sync = function() {
var self = this
this.async(function(done) {
self.sequelize
.sync({force: true})
.success(done)
.failure(function(err) { console.log(err) })
})
}
Helpers.prototype.drop = function() {
var self = this
this.async(function(done) {
self.sequelize
.drop()
.on('success', done)
.on('error', function(err) { console.log(err) })
})
}
Helpers.prototype.dropAllTables = function() {
var self = this
this.async(function(done) {
self.sequelize
.getQueryInterface()
.dropAllTables()
.success(done)
.error(function(err) { console.log(err) })
})
}
Helpers.prototype.async = function(fct) {
var done = false
runs(function() {
fct(function() { return done = true })
})
waitsFor(function(){ return done })
}
if(typeof require === 'function') {
const buster = require("buster")
, semver = require("semver")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index')
, noDomains = semver.lt(process.version, '0.8.0')
describe(Helpers.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() {
it('when we don\'t have the correct server details', function(done) {
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
expect('').toEqual('') // Silence Buster!
return done()
}
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to find MySQL server. Please double check your settings.'
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('when we don\'t have the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") {
console.log('This dialect doesn\'t support me :(')
expect('').toEqual('') // Silence Buster
return done()
}
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
expect('').toEqual('') // Silence Buster!
return done()
}
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
}
else if (dialect === "mysql") {
msg = 'Failed to authenticate for MySQL. Please double check your settings.'
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.success(function(){})
})
})
it('when we don\'t have a valid dialect.', function() {
Helpers.assertException(function() {
new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {host: '0.0.0.1', port: config[dialect].port, dialect: undefined})
}.bind(this), 'The dialect undefined is not supported.')
})
})
describe('Instantiation with a URL string', function() {
it('should accept username, password, host, port, and database', function() {
var sequelize = new Sequelize('mysql://user:pass@example.com:9821/dbname')
var config = sequelize.config
var options = sequelize.options
expect(options.dialect).toEqual('mysql')
expect(config.database).toEqual('dbname')
expect(config.host).toEqual('example.com')
expect(config.username).toEqual('user')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(9821)
})
it('should work with no authentication options', function() {
var sequelize = new Sequelize('mysql://example.com:9821/dbname')
var config = sequelize.config
expect(config.username).toEqual(undefined)
expect(config.password).toEqual(null)
})
it('should use the default port when no other is specified', function() {
var sequelize = new Sequelize('mysql://example.com/dbname')
var config = sequelize.config
// The default port should be set
expect(config.port).toEqual(3306)
})
})
describe('Intantiation with arguments', function() {
it('should accept two parameters (database, username)', function() {
var sequelize = new Sequelize('dbname', 'root')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
})
it('should accept three parameters (database, username, password)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass')
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
})
it('should accept four parameters (database, username, password, options)', function() {
var sequelize = new Sequelize('dbname', 'root', 'pass', { port: 999 })
var config = sequelize.config
expect(config.database).toEqual('dbname')
expect(config.username).toEqual('root')
expect(config.password).toEqual('pass')
expect(config.port).toEqual(999)
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, Sequelize = require("../index")
, Helpers = require('./buster-helpers')
, 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()
})
})
})
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, config = require('../config/config')
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] DAOFactory', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = self.sequelize.define('User', { age: DataTypes.INTEGER, name: DataTypes.STRING, bio: DataTypes.TEXT })
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
describe('constructor', function() {
it("handles extended attributes (unique)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: { type: Helpers.Sequelize.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (default)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (null)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (comment)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (primaryKey)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: Helpers.Sequelize.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).toEqual({username:"VARCHAR(255) PRIMARY KEY"})
done()
})
it("adds timestamps", function(done) {
var User1 = this.sequelize.define('User' + config.rand(), {})
var User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true })
expect(User1.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
expect(User2.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("adds deletedAt if paranoid", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true })
expect(User.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("underscores timestamps if underscored", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true })
expect(User.attributes).toEqual({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
done()
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
foo: {type: Helpers.Sequelize.STRING, primaryKey: true},
bar: Helpers.Sequelize.STRING
})
expect(User.primaryKeys).toEqual({"foo":"VARCHAR(255) PRIMARY KEY"})
done()
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] hstore', function() {
const hstore = require('../../lib/dialects/postgres/hstore')
describe('stringifyPart', function() {
it("handles undefined values correctly", function() {
expect(hstore.stringifyPart(undefined)).toEqual('NULL')
})
it("handles null values correctly", function() {
expect(hstore.stringifyPart(null)).toEqual('NULL')
})
it("handles boolean values correctly", function() {
expect(hstore.stringifyPart(false)).toEqual('false')
expect(hstore.stringifyPart(true)).toEqual('true')
})
it("handles strings correctly", function() {
expect(hstore.stringifyPart('foo')).toEqual('"foo"')
})
it("handles strings with backslashes correctly", function() {
expect(hstore.stringifyPart("\\'literally\\'")).toEqual('"\\\\\'literally\\\\\'"')
})
it("handles arrays correctly", function() {
expect(hstore.stringifyPart([1,['2'],'"3"'])).toEqual('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
})
it("handles simple objects correctly", function() {
expect(hstore.stringifyPart({ test: 'value' })).toEqual('"{\\"test\\":\\"value\\"}"')
})
it("handles nested objects correctly", function () {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).toEqual('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
})
it("handles objects correctly", function() {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).toEqual('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function() {
expect(hstore.stringify({ })).toEqual('')
})
it('should handle null values correctly', function () {
expect(hstore.stringify({ null: null })).toEqual('"null"=>NULL')
})
it('should handle simple objects correctly', function() {
expect(hstore.stringify({ test: 'value' })).toEqual('"test"=>"value"')
})
it('should handle nested objects correctly', function() {
expect(hstore.stringify({ test: { nested: 'value' } })).toEqual('"test"=>"{\\"nested\\":\\"value\\"}"')
})
it('should handle nested arrays correctly', function() {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).toEqual('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).toEqual('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
})
})
describe('parse', function() {
it('should handle empty objects correctly', function() {
expect(hstore.parse('')).toEqual({ })
})
it('should handle simple objects correctly', function() {
expect(hstore.parse('"test"=>"value"')).toEqual({ test: 'value' })
})
it('should handle nested objects correctly', function() {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).toEqual({ test: { nested: 'value' } })
})
it('should handle nested arrays correctly', function() {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).toEqual({ test: [ 1, '2', [ '"3"' ] ] })
})
it('should handle multiple keys with different types of values', function() {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).toEqual({ true: true, false: false, null: null, undefined: null, integer: 1, array: [1,'2'], object: { object: 'value' }})
})
})
})
}
if (typeof require === 'function') {
const buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
, _ = require('lodash')
}
buster.spec.expose()
describe(Helpers.getTestDialectTeaser("Promise"), function () {
before(function (done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function (sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: { type: DataTypes.STRING },
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER },
validateTest: {
type: DataTypes.INTEGER,
allowNull: true,
validate: {isInt: true}
},
validateCustom: {
type: DataTypes.STRING,
allowNull: true,
validate: {len: {msg: 'Length failed.', args: [1, 20]}}
},
dateAllowNullTrue: {
type: DataTypes.DATE,
allowNull: true
}
})
self.HistoryLog = sequelize.define('HistoryLog', {
someText: { type: DataTypes.STRING },
aNumber: { type: DataTypes.INTEGER },
aRandomId: { type: DataTypes.INTEGER }
})
self.ParanoidUser = sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, {
paranoid: true
})
self.ParanoidUser.hasOne(self.ParanoidUser)
},
onComplete: function () {
self.User.sync({ force: true }).then(function () {
return self.HistoryLog.sync({ force: true })
}).then(function () {
return self.ParanoidUser.sync({force: true })
})
.then(function () {done()}, done)
}
})
})
describe('increment', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment(['aNumber'], 2)
}).then(function (user2) {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(2);
done();
}, done);
});
it('should still work right with other concurrent updates', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
// Select the user again (simulating a concurrent query)
return self.User.find(1).then(function (user2) {
return user2.updateAttributes({
aNumber: user2.aNumber + 1
}).then(function (user3) {
return user1.increment(['aNumber'], 2)
}).then(function (user4) {
return self.User.find(1)
}).then(function (user5) {
expect(user5.aNumber).toBe(3);
done();
}, done)
});
});
});
it('with key value pair', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.increment({ 'aNumber': 1, 'bNumber': 2})
}).then(function () {
return self.User.find(1)
}).then(function (user3) {
expect(user3.aNumber).toBe(1);
expect(user3.bNumber).toBe(2);
done();
}, done);
});
});
describe('decrement', function () {
before(function (done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).done(done)
});
it('with array', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('with single field', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
return user1.decrement(['aNumber'], 2)
}).then(function () {
return self.User.find(1);
}).then(function (user3) {
expect(user3.aNumber).toBe(-2);
done();
}, done);
});
it('should still work right with other concurrent decrements', function (done) {
var self = this;
// Select something
this.User.find(1).then(function (user1) {
var _done = _.after(3, function () {
self.User.find(1).then(function (user2) {
expect(user2.aNumber).toEqual(-6);
done();
})
});
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
user1.decrement(['aNumber'], 2).done(_done);
});
});
});
describe('reload', function () {
it("should return a reference to the same DAO instead of creating a new one", function (done) {
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return originalUser.updateAttributes({ username: 'Doe John' }).then(function () {
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser === updatedUser).toBeTrue()
done();
}, done)
})
})
it("should update the values on all references to the DAO", function (done) {
var self = this
this.User.create({ username: 'John Doe' }).then(function (originalUser) {
return self.User.find(originalUser.id).then(function (updater) {
return updater.updateAttributes({ username: 'Doe John' })
}).then(function () {
// We used a different reference when calling updateAttributes, so originalUser is now out of sync
expect(originalUser.username).toEqual('John Doe')
return originalUser.reload()
}).then(function (updatedUser) {
expect(originalUser.username).toEqual('Doe John')
expect(updatedUser.username).toEqual('Doe John')
done();
}, done)
})
})
it("should update the associations as well", function (done) {
var Book = this.sequelize.define('Book', { title: Helpers.Sequelize.STRING })
, Page = this.sequelize.define('Page', { content: Helpers.Sequelize.TEXT })
Book.hasMany(Page)
Page.belongsTo(Book)
this.sequelize.sync({ force: true }).then(function () {
return Book.create({ title: 'A very old book' })
}).then(function (book) {
return Page.create({ content: 'om nom nom' }).then(function (page) {
return book.setPages([ page ]).then(function () {
return Book.find({
where: (dialect === 'postgres' ? '"Books"."id"=' : '`Books`.`id`=') + book.id,
include: [Page]
}).then(function (leBook) {
return page.updateAttributes({ content: 'something totally different' }).then(function (page) {
expect(leBook.pages[0].content).toEqual('om nom nom')
expect(page.content).toEqual('something totally different')
return leBook.reload().then(function (leBook) {
expect(leBook.pages[0].content).toEqual('something totally different')
expect(page.content).toEqual('something totally different')
done()
})
})
})
})
})
}, done)
})
});
describe('complete', function () {
it("gets triggered if an error occurs", function (done) {
this.User.find({ where: "asdasdasd" }).then(null, function (err) {
expect(err).toBeDefined()
expect(err.message).toBeDefined()
done()
})
})
it("gets triggered if everything was ok", function (done) {
this.User.count().then(function (result) {
expect(result).toBeDefined()
done()
})
})
})
describe('save', function () {
it('should fail a validation upon creating', function (done) {
this.User.create({aNumber: 0, validateTest: 'hello'}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer')).toBeGreaterThan(-1);
done();
});
})
it('should fail a validation upon building', function (done) {
this.User.build({aNumber: 0, validateCustom: 'aaaaaaaaaaaaaaaaaaaaaaaaaa'}).save()
.then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateCustom).toBeDefined()
expect(err.validateCustom).toBeArray()
expect(err.validateCustom[0]).toBeDefined()
expect(err.validateCustom[0]).toEqual('Length failed.')
done()
})
})
it('should fail a validation when updating', function (done) {
this.User.create({aNumber: 0}).then(function (user) {
return user.updateAttributes({validateTest: 'hello'})
}).then(null, function (err) {
expect(err).toBeDefined()
expect(err).toBeObject()
expect(err.validateTest).toBeDefined()
expect(err.validateTest).toBeArray()
expect(err.validateTest[0]).toBeDefined()
expect(err.validateTest[0].indexOf('Invalid integer:')).toBeGreaterThan(-1)
done()
})
})
})
})
if(typeof require === 'function') {
const buster = require("buster")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.getTestDialectTeaser("QueryInterface"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize) {
this.sequelize = sequelize
}.bind(this),
onComplete: function() {
this.interface = this.sequelize.getQueryInterface()
done()
}.bind(this)
})
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
this.interface.createTable('table', { name: Helpers.Sequelize.STRING }).complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(1)
this.interface.dropAllTables().complete(function(err) {
expect(err).toBeNull()
this.interface.showAllTables().complete(function(err, tableNames) {
expect(err).toBeNull()
expect(tableNames.length).toEqual(0)
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('indexes', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN
}).success(done)
})
it('adds, reads and removes an index to the table', function(done) {
this.interface.addIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
var indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual(['user_username_is_admin'])
this.interface.removeIndex('User', ['username', 'isAdmin']).complete(function(err) {
expect(err).toBeNull()
this.interface.showIndex('User').complete(function(err, indexes) {
expect(err).toBeNull()
indexColumns = Helpers.Sequelize.Utils._.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).toEqual([])
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
})
describe('describeTable', function() {
before(function(done) {
this.interface.createTable('User', {
username: Helpers.Sequelize.STRING,
isAdmin: Helpers.Sequelize.BOOLEAN,
enumVals: Helpers.Sequelize.ENUM('hello', 'world')
}).success(done)
})
it('reads the metadata of the table', function(done) {
this.interface.describeTable('User').complete(function(err, metadata) {
expect(err).toBeNull()
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).toEqual(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).toBeTrue()
expect(username.defaultValue).toBeNull()
expect(isAdmin.type).toEqual(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).toBeTrue()
expect(isAdmin.defaultValue).toBeNull()
if (dialect === 'postgres') {
expect(enumVals.special).toBeArray();
expect(enumVals.special.length).toEqual(2);
}
done()
})
})
})
})
module.exports = {
up: function(migration, DataTypes, done) {
migration.addColumn('User', 'uniqueName', { type: DataTypes.STRING }).complete(function() {
migration.changeColumn('User', 'uniqueName', { type: DataTypes.STRING, allowNull: false, unique: true }).complete(done)
})
},
down: function(migration, DataTypes, done) {
migration.removeColumn('User', 'uniqueName').complete(done)
}
}
......@@ -6,7 +6,7 @@ module.exports = {
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()
})
})
})
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'] },
......@@ -44,9 +32,9 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
pass: "129.89.23.1"
}
, isIPv6 : {
fail: '1111:2222:3333::5555:',
pass: 'fe80:0000:0000:0000:0204:61ff:fe9d:f156'
}
fail: '1111:2222:3333::5555:',
pass: 'fe80:0000:0000:0000:0204:61ff:fe9d:f156'
}
, isAlpha : {
fail: "012",
pass: "abc"
......@@ -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")) {
......@@ -202,7 +192,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var i = 0; i < validatorDetails.fail.length; i++) {
var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() {
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {}
, message = validator + "(" + failingValue + ")"
......@@ -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
......@@ -224,18 +214,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = UserFail.build({ name : failingValue })
, errors = failingUser.validate()
expect(errors).not.toBeNull()
expect(errors).toEqual({ name : [message] })
expect(errors).not.to.be.null
expect(errors).to.eql({ name : [message] })
done()
})
}
////////////////////////////
// test the success cases //
////////////////////////////
for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() {
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {}
if (validatorDetails.hasOwnProperty('spec')) {
......@@ -246,7 +238,7 @@ 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
......@@ -254,14 +246,62 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
})
var successfulUser = UserSuccess.build({ name: succeedingValue })
expect(successfulUser.validate()).toBeNull()
expect(successfulUser.validate()).to.be.null
done()
})
}
}
}
it('correctly validates using custom validation methods', function() {
var User = this.sequelize.define('User' + Math.random(), {
describe('#create', function() {
beforeEach(function(done) {
var self = this
var Project = this.sequelize.define('Project', {
name: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: 'unknown',
validate: {
isIn: [['unknown', 'hello', 'test']]
}
}
})
var Task = this.sequelize.define('Task', {
something: Sequelize.INTEGER
})
Project.hasOne(Task)
Task.hasOne(Project)
Project.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
self.Project = Project
self.Task = Task
done()
})
})
})
it('correctly validates using create method ', function(done) {
var self = this
this.Project.create({}).success(function(project) {
self.Task.create({something: 1}).success(function(task) {
project.setTask(task).success(function(task) {
expect(task.ProjectId).to.not.be.null
task.setProject(project).success(function(project) {
expect(project.ProjectId).to.not.be.null
done()
})
})
})
})
})
})
it('correctly validates using custom validation methods', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
name: {
type: Sequelize.STRING,
validate: {
......@@ -277,15 +317,17 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = User.build({ name : "3" })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ name: ["name should equal '2'"] })
expect(errors).not.to.be.null
expect(errors).to.eql({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" })
expect(successfulUser.validate()).toBeNull()
expect(successfulUser.validate()).to.be.null
done()
})
it('skips other validations if allowNull is true and the value is null', function() {
var User = this.sequelize.define('User' + Math.random(), {
it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
age: {
type: Sequelize.INTEGER,
allowNull: true,
......@@ -298,18 +340,20 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingUser = User.build({ age: -1 })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null)
expect(errors).toEqual({ age: ['must be positive'] })
expect(errors).not.to.be.null
expect(errors).to.eql({ age: ['must be positive'] })
var successfulUser1 = User.build({ age: null })
expect(successfulUser1.validate()).toBeNull()
expect(successfulUser1.validate()).to.be.null
var successfulUser2 = User.build({ age: 1 })
expect(successfulUser2.validate()).toBeNull()
expect(successfulUser2.validate()).to.be.null
done()
})
it('validates a model with custom model-wide validation methods', function() {
var Foo = this.sequelize.define('Foo' + Math.random(), {
it('validates a model with custom model-wide validation methods', function(done) {
var Foo = this.sequelize.define('Foo' + config.rand(), {
field1: {
type: Sequelize.INTEGER,
allowNull: true
......@@ -331,11 +375,12 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
var failingFoo = Foo.build({ field1: null, field2: null })
, errors = failingFoo.validate()
expect(errors).not.toBeNull()
expect(errors).toEqual({ 'xnor': ['xnor failed'] })
expect(errors).not.to.be.null
expect(errors).to.eql({ 'xnor': ['xnor failed'] })
var successfulFoo = Foo.build({ field1: 33, field2: null })
expect(successfulFoo.validate()).toBeNull()
expect(successfulFoo.validate()).to.be.null
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")
, CustomEventEmitter = require("../../lib/emitters/custom-event-emitter")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 1000
describe(Helpers.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) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, success = this.spy()
emitter.success(success)
proxy.success(function () {
process.nextTick(function () {
expect(success.called).toEqual(true)
done();
})
})
proxy.proxy(emitter)
proxy.emit('success')
})
it("should correctly work with error listeners", function (done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, error = this.spy()
emitter.error(error)
proxy.error(function () {
process.nextTick(function () {
expect(error.called).toEqual(true)
done();
})
})
proxy.proxy(emitter)
proxy.emit('error')
})
it("should correctly work with complete/done listeners", function (done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, complete = this.spy()
emitter.complete(complete)
proxy.complete(function () {
process.nextTick(function () {
expect(complete.called).toEqual(true)
done();
})
})
proxy.proxy(emitter)
proxy.emit('success')
})
});
});
\ No newline at end of file
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, sinon = require('sinon')
, CustomEventEmitter = require("../../lib/emitters/custom-event-emitter")
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("CustomEventEmitter"), function () {
describe("proxy", function () {
it("should correctly work with success listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, success = sinon.spy()
emitter.success(success)
proxy.success(function () {
process.nextTick(function () {
expect(success.called).to.be.true
done()
})
})
proxy.proxy(emitter)
proxy.emit('success')
})
it("should correctly work with error listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, error = sinon.spy()
emitter.error(error)
proxy.error(function() {
process.nextTick(function() {
expect(error.called).to.be.true
done()
})
})
proxy.proxy(emitter)
proxy.emit('error')
})
it("should correctly work with complete/done listeners", function(done) {
var emitter = new CustomEventEmitter()
, proxy = new CustomEventEmitter()
, complete = sinon.spy()
emitter.complete(complete)
proxy.complete(function() {
process.nextTick(function() {
expect(complete.called).to.be.true
done()
})
})
proxy.proxy(emitter)
proxy.emit('success')
})
})
})
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()
})
})
})
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()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose()
buster.testRunner.timeout = 1000
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] associations', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
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: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING})
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)
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
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() {
before(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING})
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.toBeDefined()
done()
Table1.sync({ force: true }).success(function() {
Table2.sync({ force: true }).success(function() {
done()
})
})
})
it("should use the specified name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
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() {
before(function(done) {
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: Helpers.Sequelize.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING })
this.User = this.sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: DataTypes.STRING })
this.users = null
this.tasks = null
......@@ -82,15 +72,19 @@ if (dialect.match(/^postgres/)) {
tasks[tasks.length] = {name: 'Task' + Math.random()}
}
this.sequelize.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(done)
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() {
before(function(done) {
beforeEach(function(done) {
var self = this
self.user = null
......@@ -109,10 +103,10 @@ if (dialect.match(/^postgres/)) {
var self = this
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
expect(_tasks.length).to.equal(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
expect(_tasks.length).to.equal(1)
done()
})
})
......@@ -121,7 +115,7 @@ if (dialect.match(/^postgres/)) {
})
describe('removeDAO', function() {
before(function(done) {
beforeEach(function(done) {
var self = this
self.user = null
......@@ -140,13 +134,13 @@ if (dialect.match(/^postgres/)) {
var self = this
self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
expect(__tasks.length).to.equal(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length)
expect(_tasks.length).to.equal(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1)
expect(_tasks.length).to.equal(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()
, sinon = require('sinon')
, DataTypes = require(__dirname + "/../../lib/data-types")
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe('[MYSQL] Connector Manager', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
describe('[MYSQL Specific] Connector Manager', function() {
this.timeout(10000)
it('works correctly after being idle', function(done) {
this.timeout = 1000 * 10
var User = this.sequelize.define('User', { username: Helpers.Sequelize.STRING })
, spy = this.spy()
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).toEqual(1)
expect(count).to.equal(1)
spy()
setTimeout(function() {
User.count().on('success', function(count) {
expect(count).toEqual(1)
expect(count).to.equal(1)
spy()
if (spy.calledTwice) {
done()
......
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe("[MYSQL Specific] DAOFactory", function () {
describe('constructor', function() {
it("handles extended attributes (unique)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: { type: DataTypes.STRING, unique: true }
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) UNIQUE",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (default)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, defaultValue: 'foo'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) DEFAULT 'foo'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (null)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, allowNull: false}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) NOT NULL",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (comment)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, comment: 'This be\'s a comment'}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) COMMENT 'This be\\'s a comment'",id:"INTEGER NOT NULL auto_increment PRIMARY KEY"})
done()
})
it("handles extended attributes (primaryKey)", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
username: {type: DataTypes.STRING, primaryKey: true}
}, { timestamps: false })
expect(User.attributes).to.deep.equal({username:"VARCHAR(255) PRIMARY KEY"})
done()
})
it("adds timestamps", function(done) {
var User1 = this.sequelize.define('User' + config.rand(), {})
var User2 = this.sequelize.define('User' + config.rand(), {}, { timestamps: true })
expect(User1.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
expect(User2.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("adds deletedAt if paranoid", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
done()
})
it("underscores timestamps if underscored", function(done) {
var User = this.sequelize.define('User' + config.rand(), {}, { paranoid: true, underscored: true })
expect(User.attributes).to.deep.equal({id:"INTEGER NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
done()
})
it('omits text fields with defaultValues', function(done) {
var User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.TEXT, defaultValue: 'helloworld'}})
expect(User.attributes.name).to.equal('TEXT')
done()
})
it('omits blobs fields with defaultValues', function(done) {
var User = this.sequelize.define('User' + config.rand(), {name: {type: DataTypes.STRING.BINARY, defaultValue: 'helloworld'}})
expect(User.attributes.name).to.equal('VARCHAR(255) BINARY')
done()
})
})
describe('validations', function() {
describe('enums', function() {
it('enum data type should be case insensitive if my collation allows it', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
mood: {
type: DataTypes.ENUM,
values: ['HAPPY', 'sad', 'WhatEver']
}
}, {
collate: 'utf8_general_ci'
})
User.sync({ force: true }).success(function() {
User.create({mood: 'happy'}).success(function(user) {
expect(user).to.exist
expect(user.mood).to.equal('HAPPY')
var u = User.build({mood: 'SAD'})
u.save().success(function(_user) {
expect(_user).to.exist
expect(_user.mood).to.equal('sad')
done()
})
})
})
})
it('enum data type should be case sensitive if my collation enforces it', function(done) {
var User = this.sequelize.define('User' + config.rand(), {
mood: {
type: DataTypes.ENUM,
values: ['HAPPY', 'sad', 'WhatEver']
}
}, {
collate: 'latin1_bin'
})
User.sync({ force: true }).success(function() {
expect(function() {
User.create({mood: 'happy'})
}).to.throw(Error, 'Value "happy" for ENUM mood is out of allowed scope. Allowed values: HAPPY, sad, WhatEver')
expect(function() {
var u = User.build({mood: 'SAD'})
u.save()
}).to.throw(Error, 'Value "SAD" for ENUM mood is out of allowed scope. Allowed values: HAPPY, sad, WhatEver')
done()
})
})
})
})
describe('primaryKeys', function() {
it("determines the correct primaryKeys", function(done) {
var User = this.sequelize.define('User' + config.rand(), {
foo: {type: DataTypes.STRING, primaryKey: true},
bar: DataTypes.STRING
})
expect(User.primaryKeys).to.deep.equal({"foo":"VARCHAR(255) PRIMARY KEY"})
done()
})
})
})
}
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, config = require('../config/config')
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/mysql/query-generator")
, util = require("util")
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")
}
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
if (dialect.match(/^mysql/)) {
describe('[MYSQL] QueryGenerator', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
describe("[MYSQL Specific] QueryGenerator", function () {
var suites = {
attributesToSQL: [
{
......@@ -85,7 +68,7 @@ if (dialect.match(/^mysql/)) {
{
arguments: [{id: {type: 'INTEGER', allowNull: false, autoIncrement: true, defaultValue: 1, references: 'Bar', onDelete: 'CASCADE', onUpdate: 'RESTRICT'}}],
expectation: {id: 'INTEGER NOT NULL auto_increment DEFAULT 1 REFERENCES `Bar` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT'}
},
}
],
createTableQuery: [
......@@ -425,7 +408,7 @@ if (dialect.match(/^mysql/)) {
]
}
Helpers.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)
......@@ -434,7 +417,7 @@ if (dialect.match(/^mysql/)) {
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()
})
})
......
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, config = require('../config/config')
, 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()
buster.testRunner.timeout = 1000
if (dialect.match(/^mysql/)) {
describe('[MYSQL] Associations', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
},
onComplete: function() {
self.sequelize.sync({ force: true }).success(done)
}
})
})
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES 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: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('wp_table1', {foo: Helpers.Sequelize.STRING})
, self = this
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)
this.sequelize.sync({ force: true }).success(function() {
expect(self.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).toBeDefined()
done()
})
expect(this.sequelize.daoFactoryManager.getDAO('wp_table1swp_table2s')).to.exist
done()
})
})
describe('when join table name is specified', function() {
before(function(done){
var Table2 = this.sequelize.define('ms_table1', {foo: Helpers.Sequelize.STRING})
, Table1 = this.sequelize.define('ms_table2', {foo: Helpers.Sequelize.STRING})
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'})
this.sequelize.sync({ force: true }).success(done)
done()
})
it("should not use a combined name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.toBeDefined()
expect(this.sequelize.daoFactoryManager.getDAO('ms_table1sms_table2s')).not.to.exist
done()
})
it("should use the specified name", function(done) {
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).toBeDefined()
expect(this.sequelize.daoFactoryManager.getDAO('table1_to_table2')).to.exist
done()
})
})
})
describe('HasMany', function() {
before(function(done) {
//prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING })
this.Task = this.sequelize.define('Task' + Math.ceil(Math.random()*10000000), { name: Helpers.Sequelize.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
var self = this
, users = []
, tasks = []
for (var i = 0; i < 5; ++i) {
users[users.length] = {name: 'User' + Math.random()}
}
for (var x = 0; x < 5; ++x) {
tasks[tasks.length] = {name: 'Task' + Math.random()}
}
this.sequelize.sync({ force: true }).success(function() {
self.User.bulkCreate(users).success(function() {
self.Task.bulkCreate(tasks).success(done)
})
})
})
describe('addDAO / getDAO', function() {
before(function(done) {
beforeEach(function(done) {
var self = this
self.user = null
self.task = null
//prevent periods from occurring in the table name since they are used to delimit (table.column)
this.User = this.sequelize.define('User' + config.rand(), { name: DataTypes.STRING })
this.Task = this.sequelize.define('Task' + config.rand(), { name: DataTypes.STRING })
this.users = null
this.tasks = null
this.User.hasMany(this.Task, {as:'Tasks'})
this.Task.hasMany(this.User, {as:'Users'})
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.task = _tasks[0]
done()
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.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()
})
})
})
})
})
})
})
......@@ -114,10 +92,10 @@ if (dialect.match(/^mysql/)) {
var self = this
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(0)
expect(_tasks).to.have.length(0)
self.user.addTask(self.task).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(1)
expect(_tasks).to.have.length(1)
done()
})
})
......@@ -126,33 +104,54 @@ if (dialect.match(/^mysql/)) {
})
describe('removeDAO', function() {
before(function(done) {
var self = this
self.user = null
self.tasks = null
self.User.all().success(function(_users) {
self.Task.all().success(function(_tasks) {
self.user = _users[0]
self.tasks = _tasks
done()
})
})
})
it("should correctly remove associated objects", function(done) {
var self = this
self.user.getTasks().on('success', function(__tasks) {
expect(__tasks.length).toEqual(0)
self.user.setTasks(self.tasks).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length)
self.user.removeTask(self.tasks[0]).on('success', function() {
self.user.getTasks().on('success', function(_tasks) {
expect(_tasks.length).toEqual(self.tasks.length - 1)
done()
, 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', {
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)
}
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"'}
})
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,61 +61,47 @@ if (dialect.match(/^postgres/)) {
.error(console.log)
})
})
})
describe('[POSTGRES] Unquoted identifiers', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.sequelize.options.quoteIdentifiers = false
self.User = sequelize.define('User', {
username: DataTypes.STRING,
fullName: DataTypes.STRING // Note mixed case
})
},
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
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')
// We can query by non-quoted identifiers
self.User.find({
where: {fullName: "John Smith"}
})
.success(function(user2) {
// 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();
})
.error(function(err) {
console.log(err)
})
describe('[POSTGRES] Unquoted identifiers', function() {
it("can insert and select", function(done) {
var self = this
this.sequelize.options.quoteIdentifiers = false
this.sequelize.getQueryInterface().QueryGenerator.options.quoteIdentifiers = false
this.User = this.sequelize.define('Userxs', {
username: DataTypes.STRING,
fullName: DataTypes.STRING // Note mixed case
}, {
quoteIdentifiers: false
})
.error(function(err) {
console.log(err)
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).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).to.equal(user.id)
expect(user2.username).to.equal('user')
expect(user2.fullName).to.equal('John Smith')
done()
})
})
})
})
})
})
}
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, hstore = require(__dirname + '/../../lib/dialects/postgres/hstore')
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES Specific] hstore', function() {
describe('stringifyPart', function() {
it("handles undefined values correctly", function(done) {
expect(hstore.stringifyPart(undefined)).to.equal('NULL')
done()
})
it("handles null values correctly", function(done) {
expect(hstore.stringifyPart(null)).to.equal('NULL')
done()
})
it("handles boolean values correctly", function(done) {
expect(hstore.stringifyPart(false)).to.equal('false')
expect(hstore.stringifyPart(true)).to.equal('true')
done()
})
it("handles strings correctly", function(done) {
expect(hstore.stringifyPart('foo')).to.equal('"foo"')
done()
})
it("handles strings with backslashes correctly", function(done) {
expect(hstore.stringifyPart("\\'literally\\'")).to.equal('"\\\\\'literally\\\\\'"')
done()
})
it("handles arrays correctly", function(done) {
expect(hstore.stringifyPart([1,['2'],'"3"'])).to.equal('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
done()
})
it("handles simple objects correctly", function(done) {
expect(hstore.stringifyPart({ test: 'value' })).to.equal('"{\\"test\\":\\"value\\"}"')
done()
})
it("handles nested objects correctly", function(done) {
expect(hstore.stringifyPart({ test: { nested: 'value' } })).to.equal('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
done()
})
it("handles objects correctly", function(done) {
expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).to.equal('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
done()
})
})
describe('stringify', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.stringify({ })).to.equal('')
done()
})
it('should handle null values correctly', function(done) {
expect(hstore.stringify({ null: null })).to.equal('"null"=>NULL')
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.stringify({ test: 'value' })).to.equal('"test"=>"value"')
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.stringify({ test: { nested: 'value' } })).to.equal('"test"=>"{\\"nested\\":\\"value\\"}"')
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).to.equal('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).to.equal('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
done()
})
})
describe('parse', function() {
it('should handle empty objects correctly', function(done) {
expect(hstore.parse('')).to.deep.equal({ })
done()
})
it('should handle simple objects correctly', function(done) {
expect(hstore.parse('"test"=>"value"')).to.deep.equal({ test: 'value' })
done()
})
it('should handle nested objects correctly', function(done) {
expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).to.deep.equal({ test: { nested: 'value' } })
done()
})
it('should handle nested arrays correctly', function(done) {
expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).to.deep.equal({ test: [ 1, '2', [ '"3"' ] ] })
done()
})
it('should handle multiple keys with different types of values', function(done) {
expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: "1", array: [1,'2'], object: { object: 'value' }})
done()
})
})
})
}
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, QueryGenerator = require("../../lib/dialects/postgres/query-generator")
, util = require("util")
, moment = require('moment')
}
/* 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')
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
if (dialect.match(/^postgres/)) {
describe('[POSTGRES] QueryGenerator', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'}
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
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()
})
})
......@@ -776,8 +768,14 @@ if (dialect.match(/^postgres/)) {
]
}
Helpers.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(done) {
......@@ -785,7 +783,7 @@ if (dialect.match(/^postgres/)) {
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')
, 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")
, 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
describe(Helpers.getTestDialectTeaser("QueryChainer"), function() {
before(function() {
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, QueryChainer = require("../lib/query-chainer")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
chai.Assertion.includeStack = true
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')
if (typeof require === 'function') {
var buster = require("buster")
, Helpers = require('./buster-helpers')
, dialect = Helpers.getTestDialect()
}
buster.spec.expose()
buster.testRunner.timeout = 2000
describe(Helpers.getTestDialectTeaser("QueryGenerators"), function() {
before(function(done) {
Helpers.initTests({
dialect: dialect,
beforeComplete: function(sequelize, DataTypes) {
this.sequelize = sequelize
this.DataTypes = DataTypes
}.bind(this),
onComplete: function() {
this.interface = this.sequelize.getQueryInterface()
done()
}.bind(this)
})
})
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: this.DataTypes.STRING, comment: 'Some lovely info for my DBA'}
username: {type: DataTypes.STRING, comment: 'Some lovely info for my DBA'}
})
User.sync().success(function(){
User.sync({ force: true }).success(function() {
var sql = ''
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\'';
......@@ -45,16 +30,16 @@ describe(Helpers.getTestDialectTeaser("QueryGenerators"), function() {
}
else if (dialect === "sqlite") {
// sqlite doesn't support comments except for explicit comments in the file
expect(true).toBeTrue()
expect(true).to.be.true
return done()
} else {
console.log('FIXME: This dialect is not supported :(');
expect(true).toBeTrue()
expect(true).to.be.true
return done()
}
self.sequelize.query(sql, null, {raw: true}).success(function(result) {
expect(result[0].cmt).toEqual('Some lovely info for my DBA');
expect(result[0].cmt).to.equal('Some lovely info for my DBA');
done()
})
})
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types")
, dialect = Support.getTestDialect()
, _ = require('lodash')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("QueryInterface"), function () {
beforeEach(function(done) {
this.sequelize.options.quoteIdenifiers = true
this.queryInterface = this.sequelize.getQueryInterface()
done()
})
describe('dropAllTables', function() {
it("should drop all tables", function(done) {
var self = this
this.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
self.queryInterface.createTable('table', { name: DataTypes.STRING }).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.have.length(1)
self.queryInterface.dropAllTables().complete(function(err) {
expect(err).to.be.null
self.queryInterface.showAllTables().complete(function(err, tableNames) {
expect(err).to.be.null
expect(tableNames).to.be.empty
done()
})
})
})
})
})
})
})
})
describe('indexes', function() {
beforeEach(function(done) {
var self = this
this.queryInterface.dropTable('Users').success(function() {
self.queryInterface.createTable('Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN
}).success(function() {
done()
})
})
})
it('adds, reads and removes an index to the table', function(done) {
var self = this
this.queryInterface.addIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
var indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.include('users_username_is_admin')
self.queryInterface.removeIndex('Users', ['username', 'isAdmin']).complete(function(err) {
expect(err).to.be.null
self.queryInterface.showIndex('Users').complete(function(err, indexes) {
expect(err).to.be.null
indexColumns = _.uniq(indexes.map(function(index) { return index.name }))
expect(indexColumns).to.be.empty
done()
})
})
})
})
})
})
describe('describeTable', function() {
it('reads the metadata of the table', function(done) {
var self = this
var Users = self.sequelize.define('_Users', {
username: DataTypes.STRING,
isAdmin: DataTypes.BOOLEAN,
enumVals: DataTypes.ENUM('hello', 'world')
}, { freezeTableName: true })
Users.sync({ force: true }).success(function() {
self.queryInterface.describeTable('_Users').complete(function(err, metadata) {
expect(err).to.be.null
var username = metadata.username
var isAdmin = metadata.isAdmin
var enumVals = metadata.enumVals
expect(username.type).to.equal(dialect === 'postgres' ? 'CHARACTER VARYING' : 'VARCHAR(255)')
expect(username.allowNull).to.be.true
expect(username.defaultValue).to.be.null
expect(isAdmin.type).to.equal(dialect === 'postgres' ? 'BOOLEAN' : 'TINYINT(1)')
expect(isAdmin.allowNull).to.be.true
expect(isAdmin.defaultValue).to.be.null
if (dialect === "postgres" || dialect === "postgres-native") {
expect(enumVals.special).to.be.instanceof(Array)
expect(enumVals.special).to.have.length(2);
}
done()
})
})
})
})
})
/* jshint camelcase: false */
if(typeof require === 'function') {
const buster = require("buster")
, Helpers = require('../buster-helpers')
, dialect = Helpers.getTestDialect()
, dbFile = __dirname + '/test.sqlite'
, storages = [dbFile]
, DataTypes = require(__dirname + "/../../lib/data-types")
}
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]
buster.spec.expose()
buster.testRunner.timeout = 1000
chai.Assertion.includeStack = true
if (dialect === 'sqlite') {
describe('[SQLITE] DAOFactory', function() {
before(function(done) {
var self = this
Helpers.initTests({
dialect: 'sqlite',
beforeComplete: function(sequelize, DataTypes) {
self.sequelize = sequelize
self.User = sequelize.define('User', {
age: DataTypes.INTEGER,
name: DataTypes.STRING,
bio: DataTypes.TEXT
})
},
onComplete: function() {
self.User.sync({ force: true }).success(done)
}
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").unlink(__dirname + '/test.sqlite', 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).toEqual(21)
expect(user.name).toEqual('John Wayne')
expect(user.bio).toEqual('noot noot')
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).toEqual(['John Wayne'])
expect(usernames).to.contain('John Wayne')
done()
})
})
......@@ -73,7 +69,7 @@ if (dialect === 'sqlite') {
name: 'John Doe',
options: options
}).success(function(people) {
expect(people.options).toEqual(options)
expect(people.options).to.deep.equal(options)
done()
})
})
......@@ -90,7 +86,7 @@ if (dialect === 'sqlite') {
name: 'John Doe',
has_swag: true
}).success(function(people) {
expect(people.has_swag).toBeTruthy();
expect(people.has_swag).to.be.ok
done()
})
})
......@@ -107,7 +103,7 @@ if (dialect === 'sqlite') {
name: 'John Doe',
has_swag: false
}).success(function(people) {
expect(people.has_swag).toBeFalsy();
expect(people.has_swag).to.not.be.ok
done()
})
})
......@@ -115,36 +111,40 @@ if (dialect === 'sqlite') {
})
describe('.find', function() {
before(function(done) {
this.User.create({name: 'user', bio: 'footbar'}).success(done)
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).toEqual('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).toEqual('user')
expect(user.username).to.equal('user')
done()
})
})
})
describe('.all', function() {
before(function(done) {
beforeEach(function(done) {
this.User.bulkCreate([
{name: 'user', bio: 'foobar'},
{name: 'user', bio: 'foobar'}
]).success(done)
]).success(function() {
done()
})
})
it("should return all users", function(done) {
this.User.all().on('success', function(users) {
expect(users.length).toEqual(2)
expect(users).to.have.length(2)
done()
})
})
......@@ -161,7 +161,7 @@ if (dialect === 'sqlite') {
this.User.bulkCreate(users).success(function() {
self.User.min('age').on('success', function(min) {
expect(min).toEqual(2)
expect(min).to.equal(2)
done()
})
})
......@@ -179,7 +179,7 @@ if (dialect === 'sqlite') {
this.User.bulkCreate(users).success(function() {
self.User.max('age').on('success', function(min) {
expect(min).toEqual(5);
expect(min).to.equal(5)
done()
})
})
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!