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

Commit e34efd47 by Andy Edwards Committed by GitHub

test: asyncify integration/*.js (#12286)

1 parent 35af6838
...@@ -29,8 +29,8 @@ if (current.dialect.supports.transactions) { ...@@ -29,8 +29,8 @@ if (current.dialect.supports.transactions) {
}); });
describe('context', () => { describe('context', () => {
it('does not use continuation storage on manually managed transactions', function() { it('does not use continuation storage on manually managed transactions', async function() {
return Sequelize._clsRun(async () => { await Sequelize._clsRun(async () => {
const transaction = await this.sequelize.transaction(); const transaction = await this.sequelize.transaction();
expect(this.ns.get('transaction')).not.to.be.ok; expect(this.ns.get('transaction')).not.to.be.ok;
await transaction.rollback(); await transaction.rollback();
...@@ -52,16 +52,15 @@ if (current.dialect.supports.transactions) { ...@@ -52,16 +52,15 @@ if (current.dialect.supports.transactions) {
expect(t1id).not.to.equal(t2id); expect(t1id).not.to.equal(t2id);
}); });
it('supports nested promise chains', function() { it('supports nested promise chains', async function() {
return this.sequelize.transaction(() => { await this.sequelize.transaction(async () => {
const tid = this.ns.get('transaction').id; const tid = this.ns.get('transaction').id;
return this.User.findAll().then(() => { await this.User.findAll();
expect(this.ns.get('transaction').id).to.be.ok; expect(this.ns.get('transaction').id).to.be.ok;
expect(this.ns.get('transaction').id).to.equal(tid); expect(this.ns.get('transaction').id).to.equal(tid);
}); });
}); });
});
it('does not leak variables to the outer scope', async function() { it('does not leak variables to the outer scope', async function() {
// This is a little tricky. We want to check the values in the outer scope, when the transaction has been successfully set up, but before it has been comitted. // This is a little tricky. We want to check the values in the outer scope, when the transaction has been successfully set up, but before it has been comitted.
...@@ -100,8 +99,8 @@ if (current.dialect.supports.transactions) { ...@@ -100,8 +99,8 @@ if (current.dialect.supports.transactions) {
expect(this.ns.get('transaction')).not.to.be.ok; expect(this.ns.get('transaction')).not.to.be.ok;
}); });
it('does not leak outside findOrCreate', function() { it('does not leak outside findOrCreate', async function() {
return this.User.findOrCreate({ await this.User.findOrCreate({
where: { where: {
name: 'Kafka' name: 'Kafka'
}, },
...@@ -110,26 +109,25 @@ if (current.dialect.supports.transactions) { ...@@ -110,26 +109,25 @@ if (current.dialect.supports.transactions) {
throw new Error('The transaction was not properly assigned'); throw new Error('The transaction was not properly assigned');
} }
} }
}).then(() => {
return this.User.findAll();
}); });
await this.User.findAll();
}); });
}); });
describe('sequelize.query integration', () => { describe('sequelize.query integration', () => {
it('automagically uses the transaction in all calls', function() { it('automagically uses the transaction in all calls', async function() {
return this.sequelize.transaction(() => { await this.sequelize.transaction(async () => {
return this.User.create({ name: 'bob' }).then(() => { await this.User.create({ name: 'bob' });
return Promise.all([ return Promise.all([
expect(this.User.findAll({ transaction: null })).to.eventually.have.length(0), expect(this.User.findAll({ transaction: null })).to.eventually.have.length(0),
expect(this.User.findAll({})).to.eventually.have.length(1) expect(this.User.findAll({})).to.eventually.have.length(1)
]); ]);
}); });
}); });
});
it('automagically uses the transaction in all calls with async/await', function() { it('automagically uses the transaction in all calls with async/await', async function() {
return this.sequelize.transaction(async () => { await this.sequelize.transaction(async () => {
await this.User.create({ name: 'bob' }); await this.User.create({ name: 'bob' });
expect(await this.User.findAll({ transaction: null })).to.have.length(0); expect(await this.User.findAll({ transaction: null })).to.have.length(0);
expect(await this.User.findAll({})).to.have.length(1); expect(await this.User.findAll({})).to.have.length(1);
...@@ -141,10 +139,11 @@ if (current.dialect.supports.transactions) { ...@@ -141,10 +139,11 @@ if (current.dialect.supports.transactions) {
expect(Sequelize._cls).to.equal(this.ns); expect(Sequelize._cls).to.equal(this.ns);
}); });
it('promises returned by sequelize.query are correctly patched', function() { it('promises returned by sequelize.query are correctly patched', async function() {
return this.sequelize.transaction(t => await this.sequelize.transaction(async t => {
this.sequelize.query('select 1', { type: Sequelize.QueryTypes.SELECT }) await this.sequelize.query('select 1', { type: Sequelize.QueryTypes.SELECT });
.then(() => expect(this.ns.get('transaction')).to.equal(t)) return expect(this.ns.get('transaction')).to.equal(t);
}
); );
}); });
}); });
......
...@@ -17,7 +17,7 @@ if (dialect === 'sqlite') { ...@@ -17,7 +17,7 @@ if (dialect === 'sqlite') {
describe(Support.getTestDialectTeaser('Configuration'), () => { describe(Support.getTestDialectTeaser('Configuration'), () => {
describe('Connections problems should fail with a nice message', () => { describe('Connections problems should fail with a nice message', () => {
it('when we don\'t have the correct server details', () => { it('when we don\'t have the correct server details', async () => {
const options = { const options = {
logging: false, logging: false,
host: '0.0.0.1', host: '0.0.0.1',
...@@ -42,10 +42,10 @@ describe(Support.getTestDialectTeaser('Configuration'), () => { ...@@ -42,10 +42,10 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
} }
const seq = new Sequelize(...constructorArgs); const seq = new Sequelize(...constructorArgs);
return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(...willBeRejectedWithArgs); await expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(...willBeRejectedWithArgs);
}); });
it('when we don\'t have the correct login information', () => { it('when we don\'t have the correct login information', async () => {
if (dialect === 'mssql') { if (dialect === 'mssql') {
// NOTE: Travis seems to be having trouble with this test against the // NOTE: Travis seems to be having trouble with this test against the
// AWS instance. Works perfectly fine on a local setup. // AWS instance. Works perfectly fine on a local setup.
...@@ -56,9 +56,10 @@ describe(Support.getTestDialectTeaser('Configuration'), () => { ...@@ -56,9 +56,10 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
const seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', { logging: false, host: config[dialect].host, port: 1, dialect }); const seq = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', { logging: false, host: config[dialect].host, port: 1, dialect });
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
// SQLite doesn't require authentication and `select 1 as hello` is a valid query, so this should be fulfilled not rejected for it. // SQLite doesn't require authentication and `select 1 as hello` is a valid query, so this should be fulfilled not rejected for it.
return expect(seq.query('select 1 as hello')).to.eventually.be.fulfilled; await expect(seq.query('select 1 as hello')).to.eventually.be.fulfilled;
} else {
await expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(Sequelize.ConnectionRefusedError, 'connect ECONNREFUSED');
} }
return expect(seq.query('select 1 as hello')).to.eventually.be.rejectedWith(Sequelize.ConnectionRefusedError, 'connect ECONNREFUSED');
}); });
it('when we don\'t have a valid dialect.', () => { it('when we don\'t have a valid dialect.', () => {
...@@ -70,7 +71,7 @@ describe(Support.getTestDialectTeaser('Configuration'), () => { ...@@ -70,7 +71,7 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
describe('Instantiation with arguments', () => { describe('Instantiation with arguments', () => {
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('should respect READONLY / READWRITE connection modes', () => { it('should respect READONLY / READWRITE connection modes', async () => {
const p = path.join(__dirname, '../tmp', 'foo.sqlite'); const p = path.join(__dirname, '../tmp', 'foo.sqlite');
const createTableFoo = 'CREATE TABLE foo (faz TEXT);'; const createTableFoo = 'CREATE TABLE foo (faz TEXT);';
const createTableBar = 'CREATE TABLE bar (baz TEXT);'; const createTableBar = 'CREATE TABLE bar (baz TEXT);';
...@@ -79,43 +80,41 @@ describe(Support.getTestDialectTeaser('Configuration'), () => { ...@@ -79,43 +80,41 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
return promisify(fs.access)(p, fs.R_OK | fs.W_OK); return promisify(fs.access)(p, fs.R_OK | fs.W_OK);
}; };
return promisify(fs.unlink)(p) try {
.catch(err => { try {
await promisify(fs.unlink)(p);
} catch (err) {
expect(err.code).to.equal('ENOENT'); expect(err.code).to.equal('ENOENT');
}) }
.then(() => {
const sequelizeReadOnly = new Sequelize('sqlite://foo', { const sequelizeReadOnly0 = new Sequelize('sqlite://foo', {
storage: p, storage: p,
dialectOptions: { dialectOptions: {
mode: sqlite3.OPEN_READONLY mode: sqlite3.OPEN_READONLY
} }
}); });
const sequelizeReadWrite = new Sequelize('sqlite://foo', { const sequelizeReadWrite0 = new Sequelize('sqlite://foo', {
storage: p, storage: p,
dialectOptions: { dialectOptions: {
mode: sqlite3.OPEN_READWRITE mode: sqlite3.OPEN_READWRITE
} }
}); });
expect(sequelizeReadOnly.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READONLY); expect(sequelizeReadOnly0.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READONLY);
expect(sequelizeReadWrite.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READWRITE); expect(sequelizeReadWrite0.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READWRITE);
return Promise.all([ await Promise.all([
sequelizeReadOnly.query(createTableFoo) sequelizeReadOnly0.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file'), .should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file'),
sequelizeReadWrite.query(createTableFoo) sequelizeReadWrite0.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file') .should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file')
]); ]);
})
.then(() => {
// By default, sqlite creates a connection that's READWRITE | CREATE // By default, sqlite creates a connection that's READWRITE | CREATE
const sequelize = new Sequelize('sqlite://foo', { const sequelize = new Sequelize('sqlite://foo', {
storage: p storage: p
}); });
return sequelize.query(createTableFoo); await testAccess(await sequelize.query(createTableFoo));
})
.then(testAccess)
.then(() => {
const sequelizeReadOnly = new Sequelize('sqlite://foo', { const sequelizeReadOnly = new Sequelize('sqlite://foo', {
storage: p, storage: p,
dialectOptions: { dialectOptions: {
...@@ -129,15 +128,14 @@ describe(Support.getTestDialectTeaser('Configuration'), () => { ...@@ -129,15 +128,14 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
} }
}); });
return Promise.all([ await Promise.all([
sequelizeReadOnly.query(createTableBar) sequelizeReadOnly.query(createTableBar)
.should.be.rejectedWith(Error, 'SQLITE_READONLY: attempt to write a readonly database'), .should.be.rejectedWith(Error, 'SQLITE_READONLY: attempt to write a readonly database'),
sequelizeReadWrite.query(createTableBar) sequelizeReadWrite.query(createTableBar)
]); ]);
}) } finally {
.finally(() => { await promisify(fs.unlink)(p);
return promisify(fs.unlink)(p); }
});
}); });
} }
}); });
......
...@@ -21,7 +21,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -21,7 +21,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
this.sequelize.connectionManager.refreshTypeParser(DataTypes[dialect]); // Reload custom parsers this.sequelize.connectionManager.refreshTypeParser(DataTypes[dialect]); // Reload custom parsers
}); });
it('allows me to return values from a custom parse function', () => { it('allows me to return values from a custom parse function', async () => {
const parse = Sequelize.DATE.parse = sinon.spy(value => { const parse = Sequelize.DATE.parse = sinon.spy(value => {
return moment(value, 'YYYY-MM-DD HH:mm:ss'); return moment(value, 'YYYY-MM-DD HH:mm:ss');
}); });
...@@ -41,13 +41,14 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -41,13 +41,14 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false timestamps: false
}); });
return current.sync({ force: true }).then(() => { await current.sync({ force: true });
return User.create({
await User.create({
dateField: moment('2011 10 31', 'YYYY MM DD') dateField: moment('2011 10 31', 'YYYY MM DD')
}); });
}).then(() => {
return User.findAll().then(obj => obj[0]); const obj = await User.findAll();
}).then(user => { const user = obj[0];
expect(parse).to.have.been.called; expect(parse).to.have.been.called;
expect(stringify).to.have.been.called; expect(stringify).to.have.been.called;
...@@ -55,9 +56,8 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -55,9 +56,8 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
delete Sequelize.DATE.parse; delete Sequelize.DATE.parse;
}); });
});
const testSuccess = function(Type, value, options) { const testSuccess = async function(Type, value, options) {
const parse = Type.constructor.parse = sinon.spy(value => { const parse = Type.constructor.parse = sinon.spy(value => {
return value; return value;
}); });
...@@ -78,16 +78,15 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -78,16 +78,15 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false timestamps: false
}); });
return current.sync({ force: true }).then(() => { await current.sync({ force: true });
current.refreshTypes(); current.refreshTypes();
return User.create({ await User.create({
field: value field: value
}); });
}).then(() => {
return User.findAll().then(obj => obj[0]); await User.findAll();
}).then(() => {
expect(parse).to.have.been.called; expect(parse).to.have.been.called;
if (options && options.useBindParam) { if (options && options.useBindParam) {
expect(bindParam).to.have.been.called; expect(bindParam).to.have.been.called;
...@@ -100,7 +99,6 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -100,7 +99,6 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
if (options && options.useBindParam) { if (options && options.useBindParam) {
delete Type.constructor.prototype.bindParam; delete Type.constructor.prototype.bindParam;
} }
});
}; };
const testFailure = function(Type) { const testFailure = function(Type) {
...@@ -114,229 +112,231 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -114,229 +112,231 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
}; };
if (current.dialect.supports.JSON) { if (current.dialect.supports.JSON) {
it('calls parse and stringify for JSON', () => { it('calls parse and stringify for JSON', async () => {
const Type = new Sequelize.JSON(); const Type = new Sequelize.JSON();
return testSuccess(Type, { test: 42, nested: { foo: 'bar' } }); await testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
}); });
} }
if (current.dialect.supports.JSONB) { if (current.dialect.supports.JSONB) {
it('calls parse and stringify for JSONB', () => { it('calls parse and stringify for JSONB', async () => {
const Type = new Sequelize.JSONB(); const Type = new Sequelize.JSONB();
return testSuccess(Type, { test: 42, nested: { foo: 'bar' } }); await testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
}); });
} }
if (current.dialect.supports.HSTORE) { if (current.dialect.supports.HSTORE) {
it('calls parse and bindParam for HSTORE', () => { it('calls parse and bindParam for HSTORE', async () => {
const Type = new Sequelize.HSTORE(); const Type = new Sequelize.HSTORE();
return testSuccess(Type, { test: 42, nested: false }, { useBindParam: true }); await testSuccess(Type, { test: 42, nested: false }, { useBindParam: true });
}); });
} }
if (current.dialect.supports.RANGE) { if (current.dialect.supports.RANGE) {
it('calls parse and bindParam for RANGE', () => { it('calls parse and bindParam for RANGE', async () => {
const Type = new Sequelize.RANGE(new Sequelize.INTEGER()); const Type = new Sequelize.RANGE(new Sequelize.INTEGER());
return testSuccess(Type, [1, 2], { useBindParam: true }); await testSuccess(Type, [1, 2], { useBindParam: true });
}); });
} }
it('calls parse and stringify for DATE', () => { it('calls parse and stringify for DATE', async () => {
const Type = new Sequelize.DATE(); const Type = new Sequelize.DATE();
return testSuccess(Type, new Date()); await testSuccess(Type, new Date());
}); });
it('calls parse and stringify for DATEONLY', () => { it('calls parse and stringify for DATEONLY', async () => {
const Type = new Sequelize.DATEONLY(); const Type = new Sequelize.DATEONLY();
return testSuccess(Type, moment(new Date()).format('YYYY-MM-DD')); await testSuccess(Type, moment(new Date()).format('YYYY-MM-DD'));
}); });
it('calls parse and stringify for TIME', () => { it('calls parse and stringify for TIME', async () => {
const Type = new Sequelize.TIME(); const Type = new Sequelize.TIME();
return testSuccess(Type, moment(new Date()).format('HH:mm:ss')); await testSuccess(Type, moment(new Date()).format('HH:mm:ss'));
}); });
it('calls parse and stringify for BLOB', () => { it('calls parse and stringify for BLOB', async () => {
const Type = new Sequelize.BLOB(); const Type = new Sequelize.BLOB();
return testSuccess(Type, 'foobar', { useBindParam: true }); await testSuccess(Type, 'foobar', { useBindParam: true });
}); });
it('calls parse and stringify for CHAR', () => { it('calls parse and stringify for CHAR', async () => {
const Type = new Sequelize.CHAR(); const Type = new Sequelize.CHAR();
return testSuccess(Type, 'foobar'); await testSuccess(Type, 'foobar');
}); });
it('calls parse and stringify/bindParam for STRING', () => { it('calls parse and stringify/bindParam for STRING', async () => {
const Type = new Sequelize.STRING(); const Type = new Sequelize.STRING();
// mssql has a _bindParam function that checks if STRING was created with // mssql has a _bindParam function that checks if STRING was created with
// the boolean param (if so it outputs a Buffer bind param). This override // the boolean param (if so it outputs a Buffer bind param). This override
// isn't needed for other dialects // isn't needed for other dialects
if (dialect === 'mssql') { if (dialect === 'mssql') {
return testSuccess(Type, 'foobar', { useBindParam: true }); await testSuccess(Type, 'foobar', { useBindParam: true });
} else {
await testSuccess(Type, 'foobar');
} }
return testSuccess(Type, 'foobar');
}); });
it('calls parse and stringify for TEXT', () => { it('calls parse and stringify for TEXT', async () => {
const Type = new Sequelize.TEXT(); const Type = new Sequelize.TEXT();
if (dialect === 'mssql') { if (dialect === 'mssql') {
// Text uses nvarchar, same type as string // Text uses nvarchar, same type as string
testFailure(Type); testFailure(Type);
} else { } else {
return testSuccess(Type, 'foobar'); await testSuccess(Type, 'foobar');
} }
}); });
it('calls parse and stringify for BOOLEAN', () => { it('calls parse and stringify for BOOLEAN', async () => {
const Type = new Sequelize.BOOLEAN(); const Type = new Sequelize.BOOLEAN();
return testSuccess(Type, true); await testSuccess(Type, true);
}); });
it('calls parse and stringify for INTEGER', () => { it('calls parse and stringify for INTEGER', async () => {
const Type = new Sequelize.INTEGER(); const Type = new Sequelize.INTEGER();
return testSuccess(Type, 1); await testSuccess(Type, 1);
}); });
it('calls parse and stringify for DECIMAL', () => { it('calls parse and stringify for DECIMAL', async () => {
const Type = new Sequelize.DECIMAL(); const Type = new Sequelize.DECIMAL();
return testSuccess(Type, 1.5); await testSuccess(Type, 1.5);
}); });
it('calls parse and stringify for BIGINT', () => { it('calls parse and stringify for BIGINT', async () => {
const Type = new Sequelize.BIGINT(); const Type = new Sequelize.BIGINT();
if (dialect === 'mssql') { if (dialect === 'mssql') {
// Same type as integer // Same type as integer
testFailure(Type); testFailure(Type);
} else { } else {
return testSuccess(Type, 1); await testSuccess(Type, 1);
} }
}); });
it('should handle JS BigInt type', function() { it('should handle JS BigInt type', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
age: Sequelize.BIGINT age: Sequelize.BIGINT
}); });
const age = BigInt(Number.MAX_SAFE_INTEGER) * 2n; const age = BigInt(Number.MAX_SAFE_INTEGER) * 2n;
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ age }); const user = await User.create({ age });
}).then(user => {
expect(BigInt(user.age).toString()).to.equal(age.toString()); expect(BigInt(user.age).toString()).to.equal(age.toString());
return User.findAll({
const users = await User.findAll({
where: { age } where: { age }
}); });
}).then(users => {
expect(users).to.have.lengthOf(1); expect(users).to.have.lengthOf(1);
expect(BigInt(users[0].age).toString()).to.equal(age.toString()); expect(BigInt(users[0].age).toString()).to.equal(age.toString());
}); });
});
if (dialect === 'mysql') { if (dialect === 'mysql') {
it('should handle TINYINT booleans', function() { it('should handle TINYINT booleans', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
id: { type: Sequelize.TINYINT, primaryKey: true }, id: { type: Sequelize.TINYINT, primaryKey: true },
isRegistered: Sequelize.TINYINT isRegistered: Sequelize.TINYINT
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ id: 1, isRegistered: true }); const registeredUser0 = await User.create({ id: 1, isRegistered: true });
}).then(registeredUser => { expect(registeredUser0.isRegistered).to.equal(true);
expect(registeredUser.isRegistered).to.equal(true);
return User.findOne({ const registeredUser = await User.findOne({
where: { where: {
id: 1, id: 1,
isRegistered: true isRegistered: true
} }
}); });
}).then(registeredUser => {
expect(registeredUser).to.be.ok; expect(registeredUser).to.be.ok;
expect(registeredUser.isRegistered).to.equal(1); expect(registeredUser.isRegistered).to.equal(1);
return User.create({ id: 2, isRegistered: false }); const unregisteredUser0 = await User.create({ id: 2, isRegistered: false });
}).then(unregisteredUser => { expect(unregisteredUser0.isRegistered).to.equal(false);
expect(unregisteredUser.isRegistered).to.equal(false);
return User.findOne({ const unregisteredUser = await User.findOne({
where: { where: {
id: 2, id: 2,
isRegistered: false isRegistered: false
} }
}); });
}).then(unregisteredUser => {
expect(unregisteredUser).to.be.ok; expect(unregisteredUser).to.be.ok;
expect(unregisteredUser.isRegistered).to.equal(0); expect(unregisteredUser.isRegistered).to.equal(0);
}); });
});
} }
it('calls parse and bindParam for DOUBLE', () => { it('calls parse and bindParam for DOUBLE', async () => {
const Type = new Sequelize.DOUBLE(); const Type = new Sequelize.DOUBLE();
return testSuccess(Type, 1.5, { useBindParam: true }); await testSuccess(Type, 1.5, { useBindParam: true });
}); });
it('calls parse and bindParam for FLOAT', () => { it('calls parse and bindParam for FLOAT', async () => {
const Type = new Sequelize.FLOAT(); const Type = new Sequelize.FLOAT();
if (dialect === 'postgres') { if (dialect === 'postgres') {
// Postgres doesn't have float, maps to either decimal or double // Postgres doesn't have float, maps to either decimal or double
testFailure(Type); testFailure(Type);
} else { } else {
return testSuccess(Type, 1.5, { useBindParam: true }); await testSuccess(Type, 1.5, { useBindParam: true });
} }
}); });
it('calls parse and bindParam for REAL', () => { it('calls parse and bindParam for REAL', async () => {
const Type = new Sequelize.REAL(); const Type = new Sequelize.REAL();
return testSuccess(Type, 1.5, { useBindParam: true }); await testSuccess(Type, 1.5, { useBindParam: true });
}); });
it('calls parse and stringify for UUID', () => { it('calls parse and stringify for UUID', async () => {
const Type = new Sequelize.UUID(); const Type = new Sequelize.UUID();
// there is no dialect.supports.UUID yet // there is no dialect.supports.UUID yet
if (['postgres', 'sqlite'].includes(dialect)) { if (['postgres', 'sqlite'].includes(dialect)) {
return testSuccess(Type, uuid.v4()); await testSuccess(Type, uuid.v4());
} } else {
// No native uuid type // No native uuid type
testFailure(Type); testFailure(Type);
}
}); });
it('calls parse and stringify for CIDR', () => { it('calls parse and stringify for CIDR', async () => {
const Type = new Sequelize.CIDR(); const Type = new Sequelize.CIDR();
if (['postgres'].includes(dialect)) { if (['postgres'].includes(dialect)) {
return testSuccess(Type, '10.1.2.3/32'); await testSuccess(Type, '10.1.2.3/32');
} } else {
testFailure(Type); testFailure(Type);
}
}); });
it('calls parse and stringify for INET', () => { it('calls parse and stringify for INET', async () => {
const Type = new Sequelize.INET(); const Type = new Sequelize.INET();
if (['postgres'].includes(dialect)) { if (['postgres'].includes(dialect)) {
return testSuccess(Type, '127.0.0.1'); await testSuccess(Type, '127.0.0.1');
} } else {
testFailure(Type); testFailure(Type);
}
}); });
it('calls parse and stringify for CITEXT', () => { it('calls parse and stringify for CITEXT', async () => {
const Type = new Sequelize.CITEXT(); const Type = new Sequelize.CITEXT();
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
...@@ -345,38 +345,41 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -345,38 +345,41 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
} }
if (dialect === 'postgres') { if (dialect === 'postgres') {
return testSuccess(Type, 'foobar'); await testSuccess(Type, 'foobar');
} } else {
testFailure(Type); testFailure(Type);
}
}); });
it('calls parse and stringify for MACADDR', () => { it('calls parse and stringify for MACADDR', async () => {
const Type = new Sequelize.MACADDR(); const Type = new Sequelize.MACADDR();
if (['postgres'].includes(dialect)) { if (['postgres'].includes(dialect)) {
return testSuccess(Type, '01:23:45:67:89:ab'); await testSuccess(Type, '01:23:45:67:89:ab');
} } else {
testFailure(Type); testFailure(Type);
}
}); });
it('calls parse and stringify for ENUM', () => { it('calls parse and stringify for ENUM', async () => {
const Type = new Sequelize.ENUM('hat', 'cat'); const Type = new Sequelize.ENUM('hat', 'cat');
if (['postgres'].includes(dialect)) { if (['postgres'].includes(dialect)) {
return testSuccess(Type, 'hat'); await testSuccess(Type, 'hat');
} } else {
testFailure(Type); testFailure(Type);
}
}); });
if (current.dialect.supports.GEOMETRY) { if (current.dialect.supports.GEOMETRY) {
it('calls parse and bindParam for GEOMETRY', () => { it('calls parse and bindParam for GEOMETRY', async () => {
const Type = new Sequelize.GEOMETRY(); const Type = new Sequelize.GEOMETRY();
return testSuccess(Type, { type: 'Point', coordinates: [125.6, 10.1] }, { useBindParam: true }); await testSuccess(Type, { type: 'Point', coordinates: [125.6, 10.1] }, { useBindParam: true });
}); });
it('should parse an empty GEOMETRY field', () => { it('should parse an empty GEOMETRY field', async () => {
const Type = new Sequelize.GEOMETRY(); const Type = new Sequelize.GEOMETRY();
// MySQL 5.7 or above doesn't support POINT EMPTY // MySQL 5.7 or above doesn't support POINT EMPTY
...@@ -384,7 +387,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -384,7 +387,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
return; return;
} }
return new Promise((resolve, reject) => { const runTests = await new Promise((resolve, reject) => {
if (/^postgres/.test(dialect)) { if (/^postgres/.test(dialect)) {
current.query('SELECT PostGIS_Lib_Version();') current.query('SELECT PostGIS_Lib_Version();')
.then(result => { .then(result => {
...@@ -397,22 +400,23 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -397,22 +400,23 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
} else { } else {
resolve(true); resolve(true);
} }
}).then(runTests => { });
if (current.dialect.supports.GEOMETRY && runTests) { if (current.dialect.supports.GEOMETRY && runTests) {
current.refreshTypes(); current.refreshTypes();
const User = current.define('user', { field: Type }, { timestamps: false }); const User = current.define('user', { field: Type }, { timestamps: false });
const point = { type: 'Point', coordinates: [] }; const point = { type: 'Point', coordinates: [] };
return current.sync({ force: true }).then(() => { await current.sync({ force: true });
return User.create({
await User.create({
//insert a empty GEOMETRY type //insert a empty GEOMETRY type
field: point field: point
}); });
}).then(() => {
//This case throw unhandled exception //This case throw unhandled exception
return User.findAll(); const users = await User.findAll();
}).then(users =>{
if (dialect === 'mysql' || dialect === 'mariadb') { if (dialect === 'mysql' || dialect === 'mariadb') {
// MySQL will return NULL, because they lack EMPTY geometry data support. // MySQL will return NULL, because they lack EMPTY geometry data support.
expect(users[0].field).to.be.eql(null); expect(users[0].field).to.be.eql(null);
...@@ -422,12 +426,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -422,12 +426,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
} else { } else {
expect(users[0].field).to.be.deep.eql(point); expect(users[0].field).to.be.deep.eql(point);
} }
});
} }
}); });
});
it('should parse null GEOMETRY field', () => { it('should parse null GEOMETRY field', async () => {
const Type = new Sequelize.GEOMETRY(); const Type = new Sequelize.GEOMETRY();
current.refreshTypes(); current.refreshTypes();
...@@ -435,48 +437,46 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -435,48 +437,46 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const User = current.define('user', { field: Type }, { timestamps: false }); const User = current.define('user', { field: Type }, { timestamps: false });
const point = null; const point = null;
return current.sync({ force: true }).then(() => { await current.sync({ force: true });
return User.create({
await User.create({
// insert a null GEOMETRY type // insert a null GEOMETRY type
field: point field: point
}); });
}).then(() => {
//This case throw unhandled exception //This case throw unhandled exception
return User.findAll(); const users = await User.findAll();
}).then(users =>{
expect(users[0].field).to.be.eql(null); expect(users[0].field).to.be.eql(null);
}); });
});
} }
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite') {
// postgres actively supports IEEE floating point literals, and sqlite doesn't care what we throw at it // postgres actively supports IEEE floating point literals, and sqlite doesn't care what we throw at it
it('should store and parse IEEE floating point literals (NaN and Infinity)', function() { it('should store and parse IEEE floating point literals (NaN and Infinity)', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
float: Sequelize.FLOAT, float: Sequelize.FLOAT,
double: Sequelize.DOUBLE, double: Sequelize.DOUBLE,
real: Sequelize.REAL real: Sequelize.REAL
}); });
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create({
await Model.create({
id: 1, id: 1,
float: NaN, float: NaN,
double: Infinity, double: Infinity,
real: -Infinity real: -Infinity
}); });
}).then(() => {
return Model.findOne({ where: { id: 1 } }); const user = await Model.findOne({ where: { id: 1 } });
}).then(user => {
expect(user.get('float')).to.be.NaN; expect(user.get('float')).to.be.NaN;
expect(user.get('double')).to.eq(Infinity); expect(user.get('double')).to.eq(Infinity);
expect(user.get('real')).to.eq(-Infinity); expect(user.get('real')).to.eq(-Infinity);
}); });
});
} }
if (dialect === 'postgres' || dialect === 'mysql') { if (dialect === 'postgres' || dialect === 'mysql') {
it('should parse DECIMAL as string', function() { it('should parse DECIMAL as string', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
decimal: Sequelize.DECIMAL, decimal: Sequelize.DECIMAL,
decimalPre: Sequelize.DECIMAL(10, 4), decimalPre: Sequelize.DECIMAL(10, 4),
...@@ -494,11 +494,9 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -494,11 +494,9 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
decimalWithFloatParser: 0.12345678 decimalWithFloatParser: 0.12345678
}; };
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create(sampleData); await Model.create(sampleData);
}).then(() => { const user = await Model.findByPk(1);
return Model.findByPk(1);
}).then(user => {
/** /**
* MYSQL default precision is 10 and scale is 0 * MYSQL default precision is 10 and scale is 0
* Thus test case below will return number without any fraction values * Thus test case below will return number without any fraction values
...@@ -514,11 +512,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -514,11 +512,10 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
expect(user.get('decimalWithIntParser')).to.be.eql('1.2340'); expect(user.get('decimalWithIntParser')).to.be.eql('1.2340');
expect(user.get('decimalWithFloatParser')).to.be.eql('0.12345678'); expect(user.get('decimalWithFloatParser')).to.be.eql('0.12345678');
}); });
});
} }
if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') {
it('should parse BIGINT as string', function() { it('should parse BIGINT as string', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
jewelPurity: Sequelize.BIGINT jewelPurity: Sequelize.BIGINT
}); });
...@@ -528,19 +525,16 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -528,19 +525,16 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
jewelPurity: '9223372036854775807' jewelPurity: '9223372036854775807'
}; };
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create(sampleData); await Model.create(sampleData);
}).then(() => { const user = await Model.findByPk(1);
return Model.findByPk(1);
}).then(user => {
expect(user.get('jewelPurity')).to.be.eql(sampleData.jewelPurity); expect(user.get('jewelPurity')).to.be.eql(sampleData.jewelPurity);
expect(user.get('jewelPurity')).to.be.string; expect(user.get('jewelPurity')).to.be.string;
}); });
});
} }
if (dialect === 'postgres') { if (dialect === 'postgres') {
it('should return Int4 range properly #5747', function() { it('should return Int4 range properly #5747', async function() {
const Model = this.sequelize.define('M', { const Model = this.sequelize.define('M', {
interval: { interval: {
type: Sequelize.RANGE(Sequelize.INTEGER), type: Sequelize.RANGE(Sequelize.INTEGER),
...@@ -549,19 +543,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -549,19 +543,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
} }
}); });
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ interval: [1, 4] }) ) await Model.create({ interval: [1, 4] });
.then(() => Model.findAll() ) const [m] = await Model.findAll();
.then(([m]) => {
expect(m.interval[0].value).to.be.eql(1); expect(m.interval[0].value).to.be.eql(1);
expect(m.interval[1].value).to.be.eql(4); expect(m.interval[1].value).to.be.eql(4);
}); });
});
} }
if (current.dialect.supports.RANGE) { if (current.dialect.supports.RANGE) {
it('should allow date ranges to be generated with default bounds inclusion #8176', function() { it('should allow date ranges to be generated with default bounds inclusion #8176', async function() {
const Model = this.sequelize.define('M', { const Model = this.sequelize.define('M', {
interval: { interval: {
type: Sequelize.RANGE(Sequelize.DATE), type: Sequelize.RANGE(Sequelize.DATE),
...@@ -573,19 +565,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -573,19 +565,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000); const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [testDate1, testDate2]; const testDateRange = [testDate1, testDate2];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ interval: testDateRange })) await Model.create({ interval: testDateRange });
.then(() => Model.findOne()) const m = await Model.findOne();
.then(m => {
expect(m).to.exist; expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1); expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2); expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(true); expect(m.interval[0].inclusive).to.be.eql(true);
expect(m.interval[1].inclusive).to.be.eql(false); expect(m.interval[1].inclusive).to.be.eql(false);
}); });
});
it('should allow date ranges to be generated using a single range expression to define bounds inclusion #8176', function() { it('should allow date ranges to be generated using a single range expression to define bounds inclusion #8176', async function() {
const Model = this.sequelize.define('M', { const Model = this.sequelize.define('M', {
interval: { interval: {
type: Sequelize.RANGE(Sequelize.DATE), type: Sequelize.RANGE(Sequelize.DATE),
...@@ -597,19 +587,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -597,19 +587,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000); const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [{ value: testDate1, inclusive: false }, { value: testDate2, inclusive: true }]; const testDateRange = [{ value: testDate1, inclusive: false }, { value: testDate2, inclusive: true }];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ interval: testDateRange })) await Model.create({ interval: testDateRange });
.then(() => Model.findOne()) const m = await Model.findOne();
.then(m => {
expect(m).to.exist; expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1); expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2); expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(false); expect(m.interval[0].inclusive).to.be.eql(false);
expect(m.interval[1].inclusive).to.be.eql(true); expect(m.interval[1].inclusive).to.be.eql(true);
}); });
});
it('should allow date ranges to be generated using a composite range expression #8176', function() { it('should allow date ranges to be generated using a composite range expression #8176', async function() {
const Model = this.sequelize.define('M', { const Model = this.sequelize.define('M', {
interval: { interval: {
type: Sequelize.RANGE(Sequelize.DATE), type: Sequelize.RANGE(Sequelize.DATE),
...@@ -621,19 +609,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -621,19 +609,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000); const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [testDate1, { value: testDate2, inclusive: true }]; const testDateRange = [testDate1, { value: testDate2, inclusive: true }];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ interval: testDateRange })) await Model.create({ interval: testDateRange });
.then(() => Model.findOne()) const m = await Model.findOne();
.then(m => {
expect(m).to.exist; expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1); expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2); expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(true); expect(m.interval[0].inclusive).to.be.eql(true);
expect(m.interval[1].inclusive).to.be.eql(true); expect(m.interval[1].inclusive).to.be.eql(true);
}); });
});
it('should correctly return ranges when using predicates that define bounds inclusion #8176', function() { it('should correctly return ranges when using predicates that define bounds inclusion #8176', async function() {
const Model = this.sequelize.define('M', { const Model = this.sequelize.define('M', {
interval: { interval: {
type: Sequelize.RANGE(Sequelize.DATE), type: Sequelize.RANGE(Sequelize.DATE),
...@@ -646,101 +632,90 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -646,101 +632,90 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDateRange = [testDate1, testDate2]; const testDateRange = [testDate1, testDate2];
const dateRangePredicate = [{ value: testDate1, inclusive: true }, { value: testDate1, inclusive: true }]; const dateRangePredicate = [{ value: testDate1, inclusive: true }, { value: testDate1, inclusive: true }];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ interval: testDateRange })) await Model.create({ interval: testDateRange });
.then(() => Model.findOne({
const m = await Model.findOne({
where: { where: {
interval: { [Op.overlap]: dateRangePredicate } interval: { [Op.overlap]: dateRangePredicate }
} }
}))
.then(m => {
expect(m).to.exist;
}); });
expect(m).to.exist;
}); });
} }
it('should allow spaces in ENUM', function() { it('should allow spaces in ENUM', async function() {
const Model = this.sequelize.define('user', { const Model = this.sequelize.define('user', {
name: Sequelize.STRING, name: Sequelize.STRING,
type: Sequelize.ENUM(['action', 'mecha', 'canon', 'class s']) type: Sequelize.ENUM(['action', 'mecha', 'canon', 'class s'])
}); });
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create({ name: 'sakura', type: 'class s' }); const record = await Model.create({ name: 'sakura', type: 'class s' });
}).then(record => {
expect(record.type).to.be.eql('class s'); expect(record.type).to.be.eql('class s');
}); });
});
it('should return YYYY-MM-DD format string for DATEONLY', function() { it('should return YYYY-MM-DD format string for DATEONLY', async function() {
const Model = this.sequelize.define('user', { const Model = this.sequelize.define('user', {
stamp: Sequelize.DATEONLY stamp: Sequelize.DATEONLY
}); });
const testDate = moment().format('YYYY-MM-DD'); const testDate = moment().format('YYYY-MM-DD');
const newDate = new Date(); const newDate = new Date();
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ stamp: testDate })) const record4 = await Model.create({ stamp: testDate });
.then(record => { expect(typeof record4.stamp).to.be.eql('string');
expect(typeof record.stamp).to.be.eql('string'); expect(record4.stamp).to.be.eql(testDate);
expect(record.stamp).to.be.eql(testDate);
return Model.findByPk(record.id); const record3 = await Model.findByPk(record4.id);
}).then(record => { expect(typeof record3.stamp).to.be.eql('string');
expect(typeof record.stamp).to.be.eql('string'); expect(record3.stamp).to.be.eql(testDate);
expect(record.stamp).to.be.eql(testDate);
return record.update({ const record2 = await record3.update({
stamp: testDate stamp: testDate
}); });
}).then(record => {
return record.reload();
}).then(record => {
expect(typeof record.stamp).to.be.eql('string');
expect(record.stamp).to.be.eql(testDate);
return record.update({ const record1 = await record2.reload();
expect(typeof record1.stamp).to.be.eql('string');
expect(record1.stamp).to.be.eql(testDate);
const record0 = await record1.update({
stamp: newDate stamp: newDate
}); });
}).then(record => {
return record.reload(); const record = await record0.reload();
}).then(record => {
expect(typeof record.stamp).to.be.eql('string'); expect(typeof record.stamp).to.be.eql('string');
const recordDate = new Date(record.stamp); const recordDate = new Date(record.stamp);
expect(recordDate.getUTCFullYear()).to.equal(newDate.getUTCFullYear()); expect(recordDate.getUTCFullYear()).to.equal(newDate.getUTCFullYear());
expect(recordDate.getUTCDate()).to.equal(newDate.getUTCDate()); expect(recordDate.getUTCDate()).to.equal(newDate.getUTCDate());
expect(recordDate.getUTCMonth()).to.equal(newDate.getUTCMonth()); expect(recordDate.getUTCMonth()).to.equal(newDate.getUTCMonth());
}); });
});
it('should return set DATEONLY field to NULL correctly', function() { it('should return set DATEONLY field to NULL correctly', async function() {
const Model = this.sequelize.define('user', { const Model = this.sequelize.define('user', {
stamp: Sequelize.DATEONLY stamp: Sequelize.DATEONLY
}); });
const testDate = moment().format('YYYY-MM-DD'); const testDate = moment().format('YYYY-MM-DD');
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => Model.create({ stamp: testDate })) const record2 = await Model.create({ stamp: testDate });
.then(record => { expect(typeof record2.stamp).to.be.eql('string');
expect(typeof record.stamp).to.be.eql('string'); expect(record2.stamp).to.be.eql(testDate);
expect(record.stamp).to.be.eql(testDate);
return Model.findByPk(record.id); const record1 = await Model.findByPk(record2.id);
}).then(record => { expect(typeof record1.stamp).to.be.eql('string');
expect(typeof record.stamp).to.be.eql('string'); expect(record1.stamp).to.be.eql(testDate);
expect(record.stamp).to.be.eql(testDate);
return record.update({ const record0 = await record1.update({
stamp: null stamp: null
}); });
}).then(record => {
return record.reload(); const record = await record0.reload();
}).then(record => {
expect(record.stamp).to.be.eql(null); expect(record.stamp).to.be.eql(null);
}); });
});
it('should be able to cast buffer as boolean', function() { it('should be able to cast buffer as boolean', async function() {
const ByteModel = this.sequelize.define('Model', { const ByteModel = this.sequelize.define('Model', {
byteToBool: this.sequelize.Sequelize.BLOB byteToBool: this.sequelize.Sequelize.BLOB
}, { }, {
...@@ -753,18 +728,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => { ...@@ -753,18 +728,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false timestamps: false
}); });
return ByteModel.sync({ await ByteModel.sync({
force: true force: true
}).then(() => { });
return ByteModel.create({
const byte = await ByteModel.create({
byteToBool: Buffer.from([true]) byteToBool: Buffer.from([true])
}); });
}).then(byte => {
expect(byte.byteToBool).to.be.ok; expect(byte.byteToBool).to.be.ok;
return BoolModel.findByPk(byte.id); const bool = await BoolModel.findByPk(byte.id);
}).then(bool => {
expect(bool.byteToBool).to.be.true; expect(bool.byteToBool).to.be.true;
}); });
});
}); });
...@@ -255,7 +255,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -255,7 +255,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}); });
describe('OptimisticLockError', () => { describe('OptimisticLockError', () => {
it('got correct error type and message', function() { it('got correct error type and message', async function() {
const Account = this.sequelize.define('Account', { const Account = this.sequelize.define('Account', {
number: { number: {
type: Sequelize.INTEGER type: Sequelize.INTEGER
...@@ -264,24 +264,23 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -264,24 +264,23 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
version: true version: true
}); });
return Account.sync({ force: true }).then(() => { await Account.sync({ force: true });
const result = Account.create({ number: 1 }).then(accountA => { const result = (async () => {
return Account.findByPk(accountA.id).then(accountB => { const accountA = await Account.create({ number: 1 });
const accountB0 = await Account.findByPk(accountA.id);
accountA.number += 1; accountA.number += 1;
return accountA.save().then(() => { return accountB; }); await accountA.save();
}); const accountB = await accountB0;
}).then(accountB => {
accountB.number += 1; accountB.number += 1;
return accountB.save(); return await accountB.save();
}); })();
return Promise.all([ await Promise.all([
expect(result).to.eventually.be.rejectedWith(Support.Sequelize.OptimisticLockError), expect(result).to.eventually.be.rejectedWith(Support.Sequelize.OptimisticLockError),
expect(result).to.eventually.be.rejectedWith('Attempting to update a stale model instance: Account') expect(result).to.eventually.be.rejectedWith('Attempting to update a stale model instance: Account')
]); ]);
}); });
}); });
});
describe('ConstraintError', () => { describe('ConstraintError', () => {
[ [
...@@ -295,7 +294,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -295,7 +294,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
} }
].forEach(constraintTest => { ].forEach(constraintTest => {
it(`Can be intercepted as ${constraintTest.type} using .catch`, function() { it(`Can be intercepted as ${constraintTest.type} using .catch`, async function() {
const spy = sinon.spy(), const spy = sinon.spy(),
User = this.sequelize.define('user', { User = this.sequelize.define('user', {
first_name: { first_name: {
...@@ -309,21 +308,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -309,21 +308,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}); });
const record = { first_name: 'jan', last_name: 'meier' }; const record = { first_name: 'jan', last_name: 'meier' };
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create(record); await User.create(record);
}).then(() => {
return User.create(record).catch(err => { try {
await User.create(record);
} catch (err) {
if (!(err instanceof constraintTest.exception)) throw err; if (!(err instanceof constraintTest.exception)) throw err;
return spy(err); await spy(err);
}); }
}).then(() => {
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
}); });
});
}); });
it('Supports newlines in keys', function() { it('Supports newlines in keys', async function() {
const spy = sinon.spy(), const spy = sinon.spy(),
User = this.sequelize.define('user', { User = this.sequelize.define('user', {
name: { name: {
...@@ -332,20 +332,20 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -332,20 +332,20 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
} }
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({ name: 'jan' }); await User.create({ name: 'jan' });
}).then(() => {
// If the error was successfully parsed, we can catch it! try {
return User.create({ name: 'jan' }).catch(err => { await User.create({ name: 'jan' });
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err; if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
return spy(err); await spy(err);
}); }
}).then(() => {
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
}); });
});
it('Works when unique keys are not defined in sequelize', function() { it('Works when unique keys are not defined in sequelize', async function() {
let User = this.sequelize.define('user', { let User = this.sequelize.define('user', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -353,23 +353,21 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -353,23 +353,21 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
} }
}, { timestamps: false }); }, { timestamps: false });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
// Now let's pretend the index was created by someone else, and sequelize doesn't know about it // Now let's pretend the index was created by someone else, and sequelize doesn't know about it
User = this.sequelize.define('user', { User = this.sequelize.define('user', {
name: Sequelize.STRING name: Sequelize.STRING
}, { timestamps: false }); }, { timestamps: false });
return User.create({ name: 'jan' }); await User.create({ name: 'jan' });
}).then(() => {
// It should work even though the unique key is not defined in the model // It should work even though the unique key is not defined in the model
return expect(User.create({ name: 'jan' })).to.be.rejectedWith(Sequelize.UniqueConstraintError); await expect(User.create({ name: 'jan' })).to.be.rejectedWith(Sequelize.UniqueConstraintError);
}).then(() => {
// And when the model is not passed at all // And when the model is not passed at all
return expect(this.sequelize.query('INSERT INTO users (name) VALUES (\'jan\')')).to.be.rejectedWith(Sequelize.UniqueConstraintError); await expect(this.sequelize.query('INSERT INTO users (name) VALUES (\'jan\')')).to.be.rejectedWith(Sequelize.UniqueConstraintError);
});
}); });
it('adds parent and sql properties', function() { it('adds parent and sql properties', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -377,28 +375,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -377,28 +375,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
} }
}, { timestamps: false }); }, { timestamps: false });
return this.sequelize.sync({ force: true }) await this.sequelize.sync({ force: true });
.then(() => { await User.create({ name: 'jan' });
return User.create({ name: 'jan' });
}).then(() => {
// Unique key // Unique key
return expect(User.create({ name: 'jan' })).to.be.rejected; const error0 = await expect(User.create({ name: 'jan' })).to.be.rejected;
}).then(error => { expect(error0).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError); expect(error0).to.have.property('parent');
expect(error).to.have.property('parent'); expect(error0).to.have.property('original');
expect(error).to.have.property('original'); expect(error0).to.have.property('sql');
expect(error).to.have.property('sql');
return User.create({ id: 2, name: 'jon' }); await User.create({ id: 2, name: 'jon' });
}).then(() => {
// Primary key // Primary key
return expect(User.create({ id: 2, name: 'jon' })).to.be.rejected; const error = await expect(User.create({ id: 2, name: 'jon' })).to.be.rejected;
}).then(error => {
expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError); expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error).to.have.property('parent'); expect(error).to.have.property('parent');
expect(error).to.have.property('original'); expect(error).to.have.property('original');
expect(error).to.have.property('sql'); expect(error).to.have.property('sql');
}); });
}); });
});
}); });
...@@ -16,53 +16,52 @@ const sortById = function(a, b) { ...@@ -16,53 +16,52 @@ const sortById = function(a, b) {
describe(Support.getTestDialectTeaser('Include'), () => { describe(Support.getTestDialectTeaser('Include'), () => {
describe('find', () => { describe('find', () => {
it('should support an empty belongsTo include', function() { it('should support an empty belongsTo include', async function() {
const Company = this.sequelize.define('Company', {}), const Company = this.sequelize.define('Company', {}),
User = this.sequelize.define('User', {}); User = this.sequelize.define('User', {});
User.belongsTo(Company, { as: 'Employer' }); User.belongsTo(Company, { as: 'Employer' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create(); await User.create();
}).then(() => {
return User.findOne({ const user = await User.findOne({
include: [{ model: Company, as: 'Employer' }] include: [{ model: Company, as: 'Employer' }]
}).then(user => {
expect(user).to.be.ok;
});
}); });
expect(user).to.be.ok;
}); });
it('should support a belongsTo association reference', function() { it('should support a belongsTo association reference', async function() {
const Company = this.sequelize.define('Company', {}), const Company = this.sequelize.define('Company', {}),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Employer = User.belongsTo(Company, { as: 'Employer' }); Employer = User.belongsTo(Company, { as: 'Employer' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create(); await User.create();
}).then(() => {
return User.findOne({ const user = await User.findOne({
include: [Employer] include: [Employer]
}).then(user => {
expect(user).to.be.ok;
});
}); });
expect(user).to.be.ok;
}); });
it('should support to use associations with Sequelize.col', function() { it('should support to use associations with Sequelize.col', async function() {
const Table1 = this.sequelize.define('Table1'); const Table1 = this.sequelize.define('Table1');
const Table2 = this.sequelize.define('Table2'); const Table2 = this.sequelize.define('Table2');
const Table3 = this.sequelize.define('Table3', { value: DataTypes.INTEGER }); const Table3 = this.sequelize.define('Table3', { value: DataTypes.INTEGER });
Table1.hasOne(Table2, { foreignKey: 'Table1Id' }); Table1.hasOne(Table2, { foreignKey: 'Table1Id' });
Table2.hasMany(Table3, { as: 'Tables3', foreignKey: 'Table2Id' }); Table2.hasMany(Table3, { as: 'Tables3', foreignKey: 'Table2Id' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Table1.create().then(table1 => { const table1 = await Table1.create();
return Table2.create({
const table2 = await Table2.create({
Table1Id: table1.get('id') Table1Id: table1.get('id')
}); });
}).then(table2 => {
return Table3.bulkCreate([ await Table3.bulkCreate([
{ {
Table2Id: table2.get('id'), Table2Id: table2.get('id'),
value: 5 value: 5
...@@ -74,9 +73,8 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -74,9 +73,8 @@ describe(Support.getTestDialectTeaser('Include'), () => {
], { ], {
validate: true validate: true
}); });
});
}).then(() => { const result = await Table1.findAll({
return Table1.findAll({
raw: true, raw: true,
attributes: [ attributes: [
[Sequelize.fn('SUM', Sequelize.col('Table2.Tables3.value')), 'sum'] [Sequelize.fn('SUM', Sequelize.col('Table2.Tables3.value')), 'sum']
...@@ -94,71 +92,68 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -94,71 +92,68 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] ]
} }
] ]
}).then(result => { });
expect(result.length).to.equal(1); expect(result.length).to.equal(1);
expect(parseInt(result[0].sum, 10)).to.eq(12); expect(parseInt(result[0].sum, 10)).to.eq(12);
}); });
});
});
it('should support a belongsTo association reference with a where', function() { it('should support a belongsTo association reference with a where', async function() {
const Company = this.sequelize.define('Company', { name: DataTypes.STRING }), const Company = this.sequelize.define('Company', { name: DataTypes.STRING }),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Employer = User.belongsTo(Company, { as: 'Employer', foreignKey: 'employerId' }); Employer = User.belongsTo(Company, { as: 'Employer', foreignKey: 'employerId' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Company.create({
const company = await Company.create({
name: 'CyberCorp' name: 'CyberCorp'
}).then(company => {
return User.create({
employerId: company.get('id')
}); });
await User.create({
employerId: company.get('id')
}); });
}).then(() => {
return User.findOne({ const user = await User.findOne({
include: [ include: [
{ association: Employer, where: { name: 'CyberCorp' } } { association: Employer, where: { name: 'CyberCorp' } }
] ]
}).then(user => {
expect(user).to.be.ok;
});
}); });
expect(user).to.be.ok;
}); });
it('should support a empty hasOne include', function() { it('should support a empty hasOne include', async function() {
const Company = this.sequelize.define('Company', {}), const Company = this.sequelize.define('Company', {}),
Person = this.sequelize.define('Person', {}); Person = this.sequelize.define('Person', {});
Company.hasOne(Person, { as: 'CEO' }); Company.hasOne(Person, { as: 'CEO' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Company.create().then(() => { await Company.create();
return Company.findOne({
const company = await Company.findOne({
include: [{ model: Person, as: 'CEO' }] include: [{ model: Person, as: 'CEO' }]
}).then(company => {
expect(company).to.be.ok;
});
});
}); });
expect(company).to.be.ok;
}); });
it('should support a hasOne association reference', function() { it('should support a hasOne association reference', async function() {
const Company = this.sequelize.define('Company', {}), const Company = this.sequelize.define('Company', {}),
Person = this.sequelize.define('Person', {}), Person = this.sequelize.define('Person', {}),
CEO = Company.hasOne(Person, { as: 'CEO' }); CEO = Company.hasOne(Person, { as: 'CEO' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Company.create(); await Company.create();
}).then(() => {
return Company.findOne({ const user = await Company.findOne({
include: [CEO] include: [CEO]
}); });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
}); });
});
it('should support including a belongsTo association rather than a model/as pair', function() { it('should support including a belongsTo association rather than a model/as pair', async function() {
const Company = this.sequelize.define('Company', {}), const Company = this.sequelize.define('Company', {}),
Person = this.sequelize.define('Person', {}); Person = this.sequelize.define('Person', {});
...@@ -166,91 +161,84 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -166,91 +161,84 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Employer: Person.belongsTo(Company, { as: 'employer' }) Employer: Person.belongsTo(Company, { as: 'employer' })
}; };
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([Person.create(), Company.create()]).then(([person, company]) => { const [person0, company] = await Promise.all([Person.create(), Company.create()]);
return person.setEmployer(company); await person0.setEmployer(company);
});
}).then(() => { const person = await Person.findOne({
return Person.findOne({
include: [Person.relation.Employer] include: [Person.relation.Employer]
}).then(person => { });
expect(person).to.be.ok; expect(person).to.be.ok;
expect(person.employer).to.be.ok; expect(person.employer).to.be.ok;
}); });
});
});
it('should support a hasMany association reference', function() { it('should support a hasMany association reference', async function() {
const User = this.sequelize.define('user', {}), const User = this.sequelize.define('user', {}),
Task = this.sequelize.define('task', {}), Task = this.sequelize.define('task', {}),
Tasks = User.hasMany(Task); Tasks = User.hasMany(Task);
Task.belongsTo(User); Task.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create().then(user => { const user0 = await User.create();
return user.createTask(); await user0.createTask();
}).then(() => {
return User.findOne({ const user = await User.findOne({
include: [Tasks] include: [Tasks]
}); });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.tasks).to.be.ok; expect(user.tasks).to.be.ok;
}); });
});
});
it('should support a hasMany association reference with a where condition', function() { it('should support a hasMany association reference with a where condition', async function() {
const User = this.sequelize.define('user', {}), const User = this.sequelize.define('user', {}),
Task = this.sequelize.define('task', { title: DataTypes.STRING }), Task = this.sequelize.define('task', { title: DataTypes.STRING }),
Tasks = User.hasMany(Task); Tasks = User.hasMany(Task);
Task.belongsTo(User); Task.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create().then(user => { const user0 = await User.create();
return Promise.all([user.createTask({
await Promise.all([user0.createTask({
title: 'trivial' title: 'trivial'
}), user.createTask({ }), user0.createTask({
title: 'pursuit' title: 'pursuit'
})]); })]);
}).then(() => {
return User.findOne({ const user = await User.findOne({
include: [ include: [
{ association: Tasks, where: { title: 'trivial' } } { association: Tasks, where: { title: 'trivial' } }
] ]
}); });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.tasks).to.be.ok; expect(user.tasks).to.be.ok;
expect(user.tasks.length).to.equal(1); expect(user.tasks.length).to.equal(1);
}); });
});
});
it('should support a belongsToMany association reference', function() { it('should support a belongsToMany association reference', async function() {
const User = this.sequelize.define('user', {}), const User = this.sequelize.define('user', {}),
Group = this.sequelize.define('group', {}), Group = this.sequelize.define('group', {}),
Groups = User.belongsToMany(Group, { through: 'UserGroup' }); Groups = User.belongsToMany(Group, { through: 'UserGroup' });
Group.belongsToMany(User, { through: 'UserGroup' }); Group.belongsToMany(User, { through: 'UserGroup' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create().then(user => { const user0 = await User.create();
return user.createGroup(); await user0.createGroup();
});
}).then(() => { const user = await User.findOne({
return User.findOne({
include: [Groups] include: [Groups]
}).then(user => { });
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.groups).to.be.ok; expect(user.groups).to.be.ok;
}); });
});
});
it('should support a simple nested belongsTo -> belongsTo include', function() { it('should support a simple nested belongsTo -> belongsTo include', async function() {
const Task = this.sequelize.define('Task', {}), const Task = this.sequelize.define('Task', {}),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {}); Group = this.sequelize.define('Group', {});
...@@ -258,15 +246,18 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -258,15 +246,18 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Task.belongsTo(User); Task.belongsTo(User);
User.belongsTo(Group); User.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return promiseProps({
const props0 = await promiseProps({
task: Task.create(), task: Task.create(),
user: User.create(), user: User.create(),
group: Group.create() group: Group.create()
}).then(props => { });
return Promise.all([props.task.setUser(props.user), props.user.setGroup(props.group)]).then(() => props);
}).then(props => { await Promise.all([props0.task.setUser(props0.user), props0.user.setGroup(props0.group)]);
return Task.findOne({ const props = props0;
const task = await Task.findOne({
where: { where: {
id: props.task.id id: props.task.id
}, },
...@@ -275,15 +266,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -275,15 +266,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
{ model: Group } { model: Group }
] } ] }
] ]
}).then(task => { });
expect(task.User).to.be.ok; expect(task.User).to.be.ok;
expect(task.User.Group).to.be.ok; expect(task.User.Group).to.be.ok;
}); });
});
});
});
it('should support a simple sibling set of belongsTo include', function() { it('should support a simple sibling set of belongsTo include', async function() {
const Task = this.sequelize.define('Task', {}), const Task = this.sequelize.define('Task', {}),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {}); Group = this.sequelize.define('Group', {});
...@@ -291,30 +280,30 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -291,30 +280,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Task.belongsTo(User); Task.belongsTo(User);
Task.belongsTo(Group); Task.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Task.create({
const task0 = await Task.create({
User: {}, User: {},
Group: {} Group: {}
}, { }, {
include: [User, Group] include: [User, Group]
}); });
}).then(task => {
return Task.findOne({ const task = await Task.findOne({
where: { where: {
id: task.id id: task0.id
}, },
include: [ include: [
{ model: User }, { model: User },
{ model: Group } { model: Group }
] ]
}); });
}).then(task => {
expect(task.User).to.be.ok; expect(task.User).to.be.ok;
expect(task.Group).to.be.ok; expect(task.Group).to.be.ok;
}); });
});
it('should support a simple nested hasOne -> hasOne include', function() { it('should support a simple nested hasOne -> hasOne include', async function() {
const Task = this.sequelize.define('Task', {}), const Task = this.sequelize.define('Task', {}),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {}); Group = this.sequelize.define('Group', {});
...@@ -323,15 +312,16 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -323,15 +312,16 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Group.hasOne(User); Group.hasOne(User);
User.belongsTo(Group); User.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({
const user = await User.create({
Task: {}, Task: {},
Group: {} Group: {}
}, { }, {
include: [Task, Group] include: [Task, Group]
}); });
}).then(user => {
return Group.findOne({ const group = await Group.findOne({
where: { where: {
id: user.Group.id id: user.Group.id
}, },
...@@ -341,13 +331,12 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -341,13 +331,12 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] } ] }
] ]
}); });
}).then(group => {
expect(group.User).to.be.ok; expect(group.User).to.be.ok;
expect(group.User.Task).to.be.ok; expect(group.User.Task).to.be.ok;
}); });
});
it('should support a simple nested hasMany -> belongsTo include', function() { it('should support a simple nested hasMany -> belongsTo include', async function() {
const Task = this.sequelize.define('Task', {}), const Task = this.sequelize.define('Task', {}),
User = this.sequelize.define('User', {}), User = this.sequelize.define('User', {}),
Project = this.sequelize.define('Project', {}); Project = this.sequelize.define('Project', {});
...@@ -355,10 +344,10 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -355,10 +344,10 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.hasMany(Task); User.hasMany(Task);
Task.belongsTo(Project); Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Project.bulkCreate([{ id: 1 }, { id: 2 }]); await Project.bulkCreate([{ id: 1 }, { id: 2 }]);
}).then(() => {
return User.create({ const user0 = await User.create({
Tasks: [ Tasks: [
{ ProjectId: 1 }, { ProjectId: 1 },
{ ProjectId: 2 }, { ProjectId: 2 },
...@@ -368,10 +357,10 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -368,10 +357,10 @@ describe(Support.getTestDialectTeaser('Include'), () => {
}, { }, {
include: [Task] include: [Task]
}); });
}).then(user => {
return User.findOne({ const user = await User.findOne({
where: { where: {
id: user.id id: user0.id
}, },
include: [ include: [
{ model: Task, include: [ { model: Task, include: [
...@@ -379,7 +368,7 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -379,7 +368,7 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] } ] }
] ]
}); });
}).then(user => {
expect(user.Tasks).to.be.ok; expect(user.Tasks).to.be.ok;
expect(user.Tasks.length).to.equal(4); expect(user.Tasks.length).to.equal(4);
...@@ -387,9 +376,8 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -387,9 +376,8 @@ describe(Support.getTestDialectTeaser('Include'), () => {
expect(task.Project).to.be.ok; expect(task.Project).to.be.ok;
}); });
}); });
});
it('should support a simple nested belongsTo -> hasMany include', function() { it('should support a simple nested belongsTo -> hasMany include', async function() {
const Task = this.sequelize.define('Task', {}), const Task = this.sequelize.define('Task', {}),
Worker = this.sequelize.define('Worker', {}), Worker = this.sequelize.define('Worker', {}),
Project = this.sequelize.define('Project', {}); Project = this.sequelize.define('Project', {});
...@@ -398,15 +386,16 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -398,15 +386,16 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Project.hasMany(Worker); Project.hasMany(Worker);
Project.hasMany(Task); Project.hasMany(Task);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Project.create({
const project = await Project.create({
Workers: [{}], Workers: [{}],
Tasks: [{}, {}, {}, {}] Tasks: [{}, {}, {}, {}]
}, { }, {
include: [Worker, Task] include: [Worker, Task]
}); });
}).then(project => {
return Worker.findOne({ const worker = await Worker.findOne({
where: { where: {
id: project.Workers[0].id id: project.Workers[0].id
}, },
...@@ -416,14 +405,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -416,14 +405,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] } ] }
] ]
}); });
}).then(worker => {
expect(worker.Project).to.be.ok; expect(worker.Project).to.be.ok;
expect(worker.Project.Tasks).to.be.ok; expect(worker.Project.Tasks).to.be.ok;
expect(worker.Project.Tasks.length).to.equal(4); expect(worker.Project.Tasks.length).to.equal(4);
}); });
});
it('should support a simple nested hasMany to hasMany include', function() { it('should support a simple nested hasMany to hasMany include', async function() {
const User = this.sequelize.define('User', {}), const User = this.sequelize.define('User', {}),
Product = this.sequelize.define('Product', { Product = this.sequelize.define('Product', {
title: DataTypes.STRING title: DataTypes.STRING
...@@ -436,8 +424,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -436,8 +424,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Product.belongsToMany(Tag, { through: 'product_tag' }); Product.belongsToMany(Tag, { through: 'product_tag' });
Tag.belongsToMany(Product, { through: 'product_tag' }); Tag.belongsToMany(Product, { through: 'product_tag' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const [products, tags] = await Promise.all([
User.create({ User.create({
id: 1, id: 1,
Products: [ Products: [
...@@ -459,14 +448,14 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -459,14 +448,14 @@ describe(Support.getTestDialectTeaser('Include'), () => {
return Tag.findAll({ order: [['id']] }); return Tag.findAll({ order: [['id']] });
}) })
]); ]);
}).then(([products, tags]) => {
return Promise.all([ await Promise.all([
products[0].setTags([tags[0], tags[2]]), products[0].setTags([tags[0], tags[2]]),
products[1].setTags([tags[1]]), products[1].setTags([tags[1]]),
products[2].setTags([tags[0], tags[1], tags[2]]) products[2].setTags([tags[0], tags[1], tags[2]])
]); ]);
}).then(() => {
return User.findOne({ const user = await User.findOne({
where: { where: {
id: 1 id: 1
}, },
...@@ -480,16 +469,15 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -480,16 +469,15 @@ describe(Support.getTestDialectTeaser('Include'), () => {
[Product, 'id'] [Product, 'id']
] ]
}); });
}).then(user => {
expect(user.Products.length).to.equal(4); expect(user.Products.length).to.equal(4);
expect(user.Products[0].Tags.length).to.equal(2); expect(user.Products[0].Tags.length).to.equal(2);
expect(user.Products[1].Tags.length).to.equal(1); expect(user.Products[1].Tags.length).to.equal(1);
expect(user.Products[2].Tags.length).to.equal(3); expect(user.Products[2].Tags.length).to.equal(3);
expect(user.Products[3].Tags.length).to.equal(0); expect(user.Products[3].Tags.length).to.equal(0);
}); });
});
it('should support an include with multiple different association types', function() { it('should support an include with multiple different association types', async function() {
const User = this.sequelize.define('User', {}), const User = this.sequelize.define('User', {}),
Product = this.sequelize.define('Product', { Product = this.sequelize.define('Product', {
title: DataTypes.STRING title: DataTypes.STRING
...@@ -534,8 +522,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -534,8 +522,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
GroupMember.belongsTo(Group); GroupMember.belongsTo(Group);
Group.hasMany(GroupMember, { as: 'Memberships' }); Group.hasMany(GroupMember, { as: 'Memberships' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const [product1, product2, user0, tags] = await Promise.all([
Product.create({ Product.create({
id: 1, id: 1,
title: 'Chair', title: 'Chair',
...@@ -563,15 +552,15 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -563,15 +552,15 @@ describe(Support.getTestDialectTeaser('Include'), () => {
return Tag.findAll(); return Tag.findAll();
}) })
]); ]);
}).then(([product1, product2, user, tags]) => {
return Promise.all([ await Promise.all([
user.setProducts([product1, product2]), user0.setProducts([product1, product2]),
product1.setTags([tags[0], tags[2]]), product1.setTags([tags[0], tags[2]]),
product2.setTags([tags[1]]), product2.setTags([tags[1]]),
product1.setCategory(tags[1]) product1.setCategory(tags[1])
]); ]);
}).then(() => {
return User.findOne({ const user = await User.findOne({
where: { id: 1 }, where: { id: 1 },
include: [ include: [
{ model: GroupMember, as: 'Memberships', include: [ { model: GroupMember, as: 'Memberships', include: [
...@@ -585,7 +574,7 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -585,7 +574,7 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] } ] }
] ]
}); });
}).then(user => {
user.Memberships.sort(sortById); user.Memberships.sort(sortById);
expect(user.Memberships.length).to.equal(2); expect(user.Memberships.length).to.equal(2);
expect(user.Memberships[0].Group.name).to.equal('Developers'); expect(user.Memberships[0].Group.name).to.equal('Developers');
...@@ -603,9 +592,8 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -603,9 +592,8 @@ describe(Support.getTestDialectTeaser('Include'), () => {
expect(user.Products[0].Prices.length).to.equal(2); expect(user.Products[0].Prices.length).to.equal(2);
expect(user.Products[1].Prices.length).to.equal(4); expect(user.Products[1].Prices.length).to.equal(4);
}); });
});
it('should support specifying attributes', function() { it('should support specifying attributes', async function() {
const Project = this.sequelize.define('Project', { const Project = this.sequelize.define('Project', {
title: Sequelize.STRING title: Sequelize.STRING
}); });
...@@ -618,30 +606,30 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -618,30 +606,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Project.hasMany(Task); Project.hasMany(Task);
Task.belongsTo(Project); Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Task.create({
await Task.create({
title: 'FooBar', title: 'FooBar',
Project: { title: 'BarFoo' } Project: { title: 'BarFoo' }
}, { }, {
include: [Project] include: [Project]
}); });
}).then(() => {
return Task.findAll({ const tasks = await Task.findAll({
attributes: ['title'], attributes: ['title'],
include: [ include: [
{ model: Project, attributes: ['title'] } { model: Project, attributes: ['title'] }
] ]
}); });
}).then(tasks => {
expect(tasks[0].title).to.equal('FooBar'); expect(tasks[0].title).to.equal('FooBar');
expect(tasks[0].Project.title).to.equal('BarFoo'); expect(tasks[0].Project.title).to.equal('BarFoo');
expect(_.omit(tasks[0].get(), 'Project')).to.deep.equal({ title: 'FooBar' }); expect(_.omit(tasks[0].get(), 'Project')).to.deep.equal({ title: 'FooBar' });
expect(tasks[0].Project.get()).to.deep.equal({ title: 'BarFoo' }); expect(tasks[0].Project.get()).to.deep.equal({ title: 'BarFoo' });
}); });
});
it('should support Sequelize.literal and renaming of attributes in included model attributes', function() { it('should support Sequelize.literal and renaming of attributes in included model attributes', async function() {
const Post = this.sequelize.define('Post', {}); const Post = this.sequelize.define('Post', {});
const PostComment = this.sequelize.define('PostComment', { const PostComment = this.sequelize.define('PostComment', {
someProperty: Sequelize.VIRTUAL, // Since we specify the AS part as a part of the literal string, not with sequelize syntax, we have to tell sequelize about the field someProperty: Sequelize.VIRTUAL, // Since we specify the AS part as a part of the literal string, not with sequelize syntax, we have to tell sequelize about the field
...@@ -650,13 +638,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -650,13 +638,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Post.hasMany(PostComment); Post.hasMany(PostComment);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Post.create({}); const post = await Post.create({});
}).then(post => {
return post.createPostComment({ await post.createPostComment({
comment_title: 'WAT' comment_title: 'WAT'
}); });
}).then(() => {
let findAttributes; let findAttributes;
if (dialect === 'mssql') { if (dialect === 'mssql') {
findAttributes = [ findAttributes = [
...@@ -671,7 +659,7 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -671,7 +659,7 @@ describe(Support.getTestDialectTeaser('Include'), () => {
} }
findAttributes.push(['comment_title', 'commentTitle']); findAttributes.push(['comment_title', 'commentTitle']);
return Post.findAll({ const posts = await Post.findAll({
include: [ include: [
{ {
model: PostComment, model: PostComment,
...@@ -679,46 +667,42 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -679,46 +667,42 @@ describe(Support.getTestDialectTeaser('Include'), () => {
} }
] ]
}); });
}).then(posts => {
expect(posts[0].PostComments[0].get('someProperty')).to.be.ok; expect(posts[0].PostComments[0].get('someProperty')).to.be.ok;
expect(posts[0].PostComments[0].get('someProperty2')).to.be.ok; expect(posts[0].PostComments[0].get('someProperty2')).to.be.ok;
expect(posts[0].PostComments[0].get('commentTitle')).to.equal('WAT'); expect(posts[0].PostComments[0].get('commentTitle')).to.equal('WAT');
}); });
});
it('should support self associated hasMany (with through) include', function() { it('should support self associated hasMany (with through) include', async function() {
const Group = this.sequelize.define('Group', { const Group = this.sequelize.define('Group', {
name: DataTypes.STRING name: DataTypes.STRING
}); });
Group.belongsToMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies' }); Group.belongsToMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies' });
const ctx = {}; await this.sequelize.sync({ force: true });
return this.sequelize.sync({ force: true }).then(() => {
return Group.bulkCreate([ await Group.bulkCreate([
{ name: 'SoccerMoms' }, { name: 'SoccerMoms' },
{ name: 'Coca Cola' }, { name: 'Coca Cola' },
{ name: 'Dell' }, { name: 'Dell' },
{ name: 'Pepsi' } { name: 'Pepsi' }
]); ]);
}).then(() => {
return Group.findAll(); const groups = await Group.findAll();
}).then(groups => { await groups[0].setOutsourcingCompanies(groups.slice(1));
ctx.groups = groups;
return groups[0].setOutsourcingCompanies(groups.slice(1)); const group = await Group.findOne({
}).then(() => {
return Group.findOne({
where: { where: {
id: ctx.groups[0].id id: groups[0].id
}, },
include: [{ model: Group, as: 'OutsourcingCompanies' }] include: [{ model: Group, as: 'OutsourcingCompanies' }]
}); });
}).then(group => {
expect(group.OutsourcingCompanies).to.have.length(3); expect(group.OutsourcingCompanies).to.have.length(3);
}); });
});
it('should support including date fields, with the correct timeszone', function() { it('should support including date fields, with the correct timeszone', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
dateField: Sequelize.DATE dateField: Sequelize.DATE
}, { timestamps: false }), }, { timestamps: false }),
...@@ -729,29 +713,27 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -729,29 +713,27 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.belongsToMany(Group, { through: 'group_user' }); User.belongsToMany(Group, { through: 'group_user' });
Group.belongsToMany(User, { through: 'group_user' }); Group.belongsToMany(User, { through: 'group_user' });
const ctx = {}; await this.sequelize.sync({ force: true });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([ const [user0, group] = await Promise.all([
User.create({ dateField: Date.UTC(2014, 1, 20) }), User.create({ dateField: Date.UTC(2014, 1, 20) }),
Group.create({ dateField: Date.UTC(2014, 1, 20) }) Group.create({ dateField: Date.UTC(2014, 1, 20) })
]); ]);
}).then(([user, group]) => {
ctx.user = user; await user0.addGroup(group);
return user.addGroup(group);
}).then(() => { const user = await User.findOne({
return User.findOne({
where: { where: {
id: ctx.user.id id: user0.id
}, },
include: [Group] include: [Group]
}); });
}).then(user => {
expect(user.dateField.getTime()).to.equal(Date.UTC(2014, 1, 20)); expect(user.dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
expect(user.groups[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20)); expect(user.groups[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
}); });
});
it('should support include when retrieving associated objects', function() { it('should support include when retrieving associated objects', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
name: DataTypes.STRING name: DataTypes.STRING
}), }),
...@@ -773,35 +755,30 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -773,35 +755,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
as: 'Members' as: 'Members'
}); });
const ctx = {}; await this.sequelize.sync({ force: true });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([ const [owner, member, group] = await Promise.all([
User.create({ name: 'Owner' }), User.create({ name: 'Owner' }),
User.create({ name: 'Member' }), User.create({ name: 'Member' }),
Group.create({ name: 'Group' }) Group.create({ name: 'Group' })
]); ]);
}).then(([owner, member, group]) => {
ctx.owner = owner; await owner.addGroup(group);
ctx.member = member; await group.addMember(member);
ctx.group = group;
return owner.addGroup(group); const groups = await owner.getGroups({
}).then(() => {
return ctx.group.addMember(ctx.member);
}).then(() => {
return ctx.owner.getGroups({
include: [{ include: [{
model: User, model: User,
as: 'Members' as: 'Members'
}] }]
}); });
}).then(groups => {
expect(groups.length).to.equal(1); expect(groups.length).to.equal(1);
expect(groups[0].Members[0].name).to.equal('Member'); expect(groups[0].Members[0].name).to.equal('Member');
}); });
}); });
});
const createUsersAndItems = function() { const createUsersAndItems = async function() {
const User = this.sequelize.define('User', {}), const User = this.sequelize.define('User', {}),
Item = this.sequelize.define('Item', { 'test': DataTypes.STRING }); Item = this.sequelize.define('Item', { 'test': DataTypes.STRING });
...@@ -811,8 +788,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -811,8 +788,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
this.User = User; this.User = User;
this.Item = Item; this.Item = Item;
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const [users, items] = await Promise.all([
User.bulkCreate([{}, {}, {}]).then(() => { User.bulkCreate([{}, {}, {}]).then(() => {
return User.findAll(); return User.findAll();
}), }),
...@@ -824,33 +802,32 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -824,33 +802,32 @@ describe(Support.getTestDialectTeaser('Include'), () => {
return Item.findAll(); return Item.findAll();
}) })
]); ]);
}).then(([users, items]) => {
return Promise.all([ return Promise.all([
users[0].setItem(items[0]), users[0].setItem(items[0]),
users[1].setItem(items[1]), users[1].setItem(items[1]),
users[2].setItem(items[2]) users[2].setItem(items[2])
]); ]);
});
}; };
describe('where', () => { describe('where', () => {
beforeEach(function() { beforeEach(async function() {
return createUsersAndItems.bind(this)(); await createUsersAndItems.bind(this)();
}); });
it('should support Sequelize.and()', function() { it('should support Sequelize.and()', async function() {
return this.User.findAll({ const result = await this.User.findAll({
include: [ include: [
{ model: this.Item, where: Sequelize.and({ test: 'def' }) } { model: this.Item, where: Sequelize.and({ test: 'def' }) }
] ]
}).then(result => { });
expect(result.length).to.eql(1); expect(result.length).to.eql(1);
expect(result[0].Item.test).to.eql('def'); expect(result[0].Item.test).to.eql('def');
}); });
});
it('should support Sequelize.or()', function() { it('should support Sequelize.or()', async function() {
return expect(this.User.findAll({ await expect(this.User.findAll({
include: [ include: [
{ model: this.Item, where: Sequelize.or({ { model: this.Item, where: Sequelize.or({
test: 'def' test: 'def'
...@@ -863,26 +840,26 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -863,26 +840,26 @@ describe(Support.getTestDialectTeaser('Include'), () => {
}); });
describe('findAndCountAll', () => { describe('findAndCountAll', () => {
it('should include associations to findAndCountAll', function() { it('should include associations to findAndCountAll', async function() {
return createUsersAndItems.bind(this)().then(() => { await createUsersAndItems.bind(this)();
return this.User.findAndCountAll({
const result = await this.User.findAndCountAll({
include: [ include: [
{ model: this.Item, where: { { model: this.Item, where: {
test: 'def' test: 'def'
} } } }
] ]
}); });
}).then(result => {
expect(result.count).to.eql(1); expect(result.count).to.eql(1);
expect(result.rows.length).to.eql(1); expect(result.rows.length).to.eql(1);
expect(result.rows[0].Item.test).to.eql('def'); expect(result.rows[0].Item.test).to.eql('def');
}); });
}); });
});
describe('association getter', () => { describe('association getter', () => {
it('should support getting an include on a N:M association getter', function() { it('should support getting an include on a N:M association getter', async function() {
const Question = this.sequelize.define('Question', {}), const Question = this.sequelize.define('Question', {}),
Answer = this.sequelize.define('Answer', {}), Answer = this.sequelize.define('Answer', {}),
Questionnaire = this.sequelize.define('Questionnaire', {}); Questionnaire = this.sequelize.define('Questionnaire', {});
...@@ -893,18 +870,17 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -893,18 +870,17 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Questionnaire.hasMany(Question); Questionnaire.hasMany(Question);
Question.belongsTo(Questionnaire); Question.belongsTo(Questionnaire);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Questionnaire.create(); const questionnaire = await Questionnaire.create();
}).then(questionnaire => {
return questionnaire.getQuestions({ await questionnaire.getQuestions({
include: Answer include: Answer
}); });
}); });
}); });
});
describe('right join', () => { describe('right join', () => {
it('should support getting an include with a right join', function() { it('should support getting an include with a right join', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
name: DataTypes.STRING name: DataTypes.STRING
}), }),
...@@ -915,30 +891,30 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -915,30 +891,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.hasMany(Group); User.hasMany(Group);
Group.belongsTo(User); Group.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
await Promise.all([
User.create({ name: 'User 1' }), User.create({ name: 'User 1' }),
User.create({ name: 'User 2' }), User.create({ name: 'User 2' }),
User.create({ name: 'User 3' }), User.create({ name: 'User 3' }),
Group.create({ name: 'A Group' }) Group.create({ name: 'A Group' })
]); ]);
}).then(() => {
return Group.findAll({ const groups = await Group.findAll({
include: [{ include: [{
model: User, model: User,
right: true right: true
}] }]
}); });
}).then(groups => {
if (current.dialect.supports['RIGHT JOIN']) { if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(3); expect(groups.length).to.equal(3);
} else { } else {
expect(groups.length).to.equal(1); expect(groups.length).to.equal(1);
} }
}); });
});
it('should support getting an include through with a right join', function() { it('should support getting an include through with a right join', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
name: DataTypes.STRING name: DataTypes.STRING
}), }),
...@@ -962,36 +938,31 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -962,36 +938,31 @@ describe(Support.getTestDialectTeaser('Include'), () => {
constraints: false constraints: false
}); });
const ctx = {}; await this.sequelize.sync({ force: true });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([ const [member1, member2, group1, group2] = await Promise.all([
User.create({ name: 'Member 1' }), User.create({ name: 'Member 1' }),
User.create({ name: 'Member 2' }), User.create({ name: 'Member 2' }),
Group.create({ name: 'Group 1' }), Group.create({ name: 'Group 1' }),
Group.create({ name: 'Group 2' }) Group.create({ name: 'Group 2' })
]); ]);
}).then(([member1, member2, group1, group2]) => {
ctx.member1 = member1; await Promise.all([
ctx.member2 = member2; group1.addMember(member1),
ctx.group1 = group1; group1.addMember(member2),
ctx.group2 = group2; group2.addMember(member1)
}).then(() => {
return Promise.all([
ctx.group1.addMember(ctx.member1),
ctx.group1.addMember(ctx.member2),
ctx.group2.addMember(ctx.member1)
]); ]);
}).then(() => {
return ctx.group2.destroy(); await group2.destroy();
}).then(() => {
return Group.findAll({ const groups = await Group.findAll({
include: [{ include: [{
model: User, model: User,
as: 'Members', as: 'Members',
right: true right: true
}] }]
}); });
}).then(groups => {
if (current.dialect.supports['RIGHT JOIN']) { if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(2); expect(groups.length).to.equal(2);
} else { } else {
...@@ -999,10 +970,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -999,10 +970,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
} }
}); });
}); });
});
describe('nested includes', () => { describe('nested includes', () => {
beforeEach(function() { beforeEach(async function() {
const Employee = this.sequelize.define('Employee', { 'name': DataTypes.STRING }); const Employee = this.sequelize.define('Employee', { 'name': DataTypes.STRING });
const Team = this.sequelize.define('Team', { 'name': DataTypes.STRING }); const Team = this.sequelize.define('Team', { 'name': DataTypes.STRING });
const Clearence = this.sequelize.define('Clearence', { 'level': DataTypes.INTEGER }); const Clearence = this.sequelize.define('Clearence', { 'level': DataTypes.INTEGER });
...@@ -1015,8 +985,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1015,8 +985,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
this.Team = Team; this.Team = Team;
this.Clearence = Clearence; this.Clearence = Clearence;
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const instances = await Promise.all([
Team.create({ name: 'TeamA' }), Team.create({ name: 'TeamA' }),
Team.create({ name: 'TeamB' }), Team.create({ name: 'TeamB' }),
Employee.create({ name: 'John' }), Employee.create({ name: 'John' }),
...@@ -1025,19 +996,18 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1025,19 +996,18 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Employee.create({ name: 'Jill' }), Employee.create({ name: 'Jill' }),
Clearence.create({ level: 3 }), Clearence.create({ level: 3 }),
Clearence.create({ level: 5 }) Clearence.create({ level: 5 })
]).then(instances => { ]);
return Promise.all([
await Promise.all([
instances[0].addMembers([instances[2], instances[3]]), instances[0].addMembers([instances[2], instances[3]]),
instances[1].addMembers([instances[4], instances[5]]), instances[1].addMembers([instances[4], instances[5]]),
instances[2].setClearence(instances[6]), instances[2].setClearence(instances[6]),
instances[3].setClearence(instances[7]) instances[3].setClearence(instances[7])
]); ]);
}); });
});
});
it('should not ripple grandchild required to top level find when required of child is set to false', function() { it('should not ripple grandchild required to top level find when required of child is set to false', async function() {
return this.Team.findAll({ const teams = await this.Team.findAll({
include: [ include: [
{ {
association: this.Team.Members, association: this.Team.Members,
...@@ -1050,13 +1020,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1050,13 +1020,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] ]
} }
] ]
}).then(teams => {
expect(teams).to.have.length(2);
}); });
expect(teams).to.have.length(2);
}); });
it('should support eager loading associations using the name of the relation (string)', function() { it('should support eager loading associations using the name of the relation (string)', async function() {
return this.Team.findOne({ const team = await this.Team.findOne({
where: { where: {
name: 'TeamA' name: 'TeamA'
}, },
...@@ -1066,13 +1036,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1066,13 +1036,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
required: true required: true
} }
] ]
}).then(team => {
expect(team.members).to.have.length(2);
}); });
expect(team.members).to.have.length(2);
}); });
it('should not ripple grandchild required to top level find when required of child is not given (implicitly false)', function() { it('should not ripple grandchild required to top level find when required of child is not given (implicitly false)', async function() {
return this.Team.findAll({ const teams = await this.Team.findAll({
include: [ include: [
{ {
association: this.Team.Members, association: this.Team.Members,
...@@ -1084,13 +1054,13 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1084,13 +1054,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] ]
} }
] ]
}).then(teams => {
expect(teams).to.have.length(2);
}); });
expect(teams).to.have.length(2);
}); });
it('should ripple grandchild required to top level find when required of child is set to true as well', function() { it('should ripple grandchild required to top level find when required of child is set to true as well', async function() {
return this.Team.findAll({ const teams = await this.Team.findAll({
include: [ include: [
{ {
association: this.Team.Members, association: this.Team.Members,
...@@ -1103,9 +1073,9 @@ describe(Support.getTestDialectTeaser('Include'), () => { ...@@ -1103,9 +1073,9 @@ describe(Support.getTestDialectTeaser('Include'), () => {
] ]
} }
] ]
}).then(teams => {
expect(teams).to.have.length(1);
}); });
expect(teams).to.have.length(1);
}); });
}); });
......
...@@ -21,7 +21,7 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -21,7 +21,7 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
this.clock.restore(); this.clock.restore();
}); });
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING }, username: { type: DataTypes.STRING },
uuidv1: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV1 }, uuidv1: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV1 },
...@@ -53,22 +53,20 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -53,22 +53,20 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
} }
}); });
return this.User.sync({ force: true }); await this.User.sync({ force: true });
}); });
describe('Escaping', () => { describe('Escaping', () => {
it('is done properly for special characters', function() { it('is done properly for special characters', async function() {
// Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a" // Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a"
// But this causes sqlite to fail and exits the entire test suite immediately // But this causes sqlite to fail and exits the entire test suite immediately
const bio = `${dialect}'"\n`; // Need to add the dialect here so in case of failure I know what DB it failed for const bio = `${dialect}'"\n`; // Need to add the dialect here so in case of failure I know what DB it failed for
return this.User.create({ username: bio }).then(u1 => { const u1 = await this.User.create({ username: bio });
return this.User.findByPk(u1.id).then(u2 => { const u2 = await this.User.findByPk(u1.id);
expect(u2.username).to.equal(bio); expect(u2.username).to.equal(bio);
}); });
}); });
});
});
describe('isNewRecord', () => { describe('isNewRecord', () => {
it('returns true for non-saved objects', function() { it('returns true for non-saved objects', function() {
...@@ -77,44 +75,37 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -77,44 +75,37 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
expect(user.isNewRecord).to.be.ok; expect(user.isNewRecord).to.be.ok;
}); });
it('returns false for saved objects', function() { it('returns false for saved objects', async function() {
return this.User.build({ username: 'user' }).save().then(user => { const user = await this.User.build({ username: 'user' }).save();
expect(user.isNewRecord).to.not.be.ok; expect(user.isNewRecord).to.not.be.ok;
}); });
});
it('returns false for created objects', function() { it('returns false for created objects', async function() {
return this.User.create({ username: 'user' }).then(user => { const user = await this.User.create({ username: 'user' });
expect(user.isNewRecord).to.not.be.ok; expect(user.isNewRecord).to.not.be.ok;
}); });
});
it('returns false for objects found by find method', function() { it('returns false for objects found by find method', async function() {
return this.User.create({ username: 'user' }).then(() => { await this.User.create({ username: 'user' });
return this.User.create({ username: 'user' }).then(user => { const user = await this.User.create({ username: 'user' });
return this.User.findByPk(user.id).then(user => { const user0 = await this.User.findByPk(user.id);
expect(user.isNewRecord).to.not.be.ok; expect(user0.isNewRecord).to.not.be.ok;
});
});
});
}); });
it('returns false for objects found by findAll method', function() { it('returns false for objects found by findAll method', async function() {
const users = []; const users = [];
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
users[i] = { username: 'user' }; users[i] = { username: 'user' };
} }
return this.User.bulkCreate(users).then(() => { await this.User.bulkCreate(users);
return this.User.findAll().then(users => { const users0 = await this.User.findAll();
users.forEach(u => { users0.forEach(u => {
expect(u.isNewRecord).to.not.be.ok; expect(u.isNewRecord).to.not.be.ok;
}); });
}); });
}); });
});
});
describe('default values', () => { describe('default values', () => {
describe('uuid', () => { describe('uuid', () => {
...@@ -174,260 +165,225 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -174,260 +165,225 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
}); });
describe('allowNull date', () => { describe('allowNull date', () => {
it('should be just "null" and not Date with Invalid Date', function() { it('should be just "null" and not Date with Invalid Date', async function() {
return this.User.build({ username: 'a user' }).save().then(() => { await this.User.build({ username: 'a user' }).save();
return this.User.findOne({ where: { username: 'a user' } }).then(user => { const user = await this.User.findOne({ where: { username: 'a user' } });
expect(user.dateAllowNullTrue).to.be.null; expect(user.dateAllowNullTrue).to.be.null;
}); });
});
});
it('should be the same valid date when saving the date', function() { it('should be the same valid date when saving the date', async function() {
const date = new Date(); const date = new Date();
return this.User.build({ username: 'a user', dateAllowNullTrue: date }).save().then(() => { await this.User.build({ username: 'a user', dateAllowNullTrue: date }).save();
return this.User.findOne({ where: { username: 'a user' } }).then(user => { const user = await this.User.findOne({ where: { username: 'a user' } });
expect(user.dateAllowNullTrue.toString()).to.equal(date.toString()); expect(user.dateAllowNullTrue.toString()).to.equal(date.toString());
}); });
}); });
});
});
describe('super user boolean', () => { describe('super user boolean', () => {
it('should default to false', function() { it('should default to false', async function() {
return this.User.build({ await this.User.build({
username: 'a user' username: 'a user'
}) })
.save() .save();
.then(() => {
return this.User.findOne({ const user = await this.User.findOne({
where: { where: {
username: 'a user' username: 'a user'
} }
})
.then(user => {
expect(user.isSuperUser).to.be.false;
});
}); });
expect(user.isSuperUser).to.be.false;
}); });
it('should override default when given truthy boolean', function() { it('should override default when given truthy boolean', async function() {
return this.User.build({ await this.User.build({
username: 'a user', username: 'a user',
isSuperUser: true isSuperUser: true
}) })
.save() .save();
.then(() => {
return this.User.findOne({ const user = await this.User.findOne({
where: { where: {
username: 'a user' username: 'a user'
} }
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
}); });
expect(user.isSuperUser).to.be.true;
}); });
it('should override default when given truthy boolean-string ("true")', function() { it('should override default when given truthy boolean-string ("true")', async function() {
return this.User.build({ await this.User.build({
username: 'a user', username: 'a user',
isSuperUser: 'true' isSuperUser: 'true'
}) })
.save() .save();
.then(() => {
return this.User.findOne({ const user = await this.User.findOne({
where: { where: {
username: 'a user' username: 'a user'
} }
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
}); });
expect(user.isSuperUser).to.be.true;
}); });
it('should override default when given truthy boolean-int (1)', function() { it('should override default when given truthy boolean-int (1)', async function() {
return this.User.build({ await this.User.build({
username: 'a user', username: 'a user',
isSuperUser: 1 isSuperUser: 1
}) })
.save() .save();
.then(() => {
return this.User.findOne({ const user = await this.User.findOne({
where: { where: {
username: 'a user' username: 'a user'
} }
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
}); });
expect(user.isSuperUser).to.be.true;
}); });
it('should throw error when given value of incorrect type', function() { it('should throw error when given value of incorrect type', async function() {
let callCount = 0; let callCount = 0;
return this.User.build({ try {
await this.User.build({
username: 'a user', username: 'a user',
isSuperUser: 'INCORRECT_VALUE_TYPE' isSuperUser: 'INCORRECT_VALUE_TYPE'
}) })
.save() .save();
.then(() => {
callCount += 1; callCount += 1;
}) } catch (err) {
.catch(err => {
expect(callCount).to.equal(0); expect(callCount).to.equal(0);
expect(err).to.exist; expect(err).to.exist;
expect(err.message).to.exist; expect(err.message).to.exist;
}); }
}); });
}); });
}); });
describe('complete', () => { describe('complete', () => {
it('gets triggered if an error occurs', function() { it('gets triggered if an error occurs', async function() {
return this.User.findOne({ where: ['asdasdasd'] }).catch(err => { try {
await this.User.findOne({ where: ['asdasdasd'] });
} catch (err) {
expect(err).to.exist; expect(err).to.exist;
expect(err.message).to.exist; expect(err.message).to.exist;
}); }
}); });
it('gets triggered if everything was ok', function() { it('gets triggered if everything was ok', async function() {
return this.User.count().then(result => { const result = await this.User.count();
expect(result).to.exist; expect(result).to.exist;
}); });
}); });
});
describe('findAll', () => { describe('findAll', () => {
beforeEach(function() { beforeEach(async function() {
this.ParanoidUser = this.sequelize.define('ParanoidUser', { this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING } username: { type: DataTypes.STRING }
}, { paranoid: true }); }, { paranoid: true });
this.ParanoidUser.hasOne(this.ParanoidUser); this.ParanoidUser.hasOne(this.ParanoidUser);
return this.ParanoidUser.sync({ force: true }); await this.ParanoidUser.sync({ force: true });
}); });
it('sql should have paranoid condition', function() { it('sql should have paranoid condition', async function() {
return this.ParanoidUser.create({ username: 'cuss' }) await this.ParanoidUser.create({ username: 'cuss' });
.then(() => { const users0 = await this.ParanoidUser.findAll();
return this.ParanoidUser.findAll(); expect(users0).to.have.length(1);
}) await users0[0].destroy();
.then(users => { const users = await this.ParanoidUser.findAll();
expect(users).to.have.length(1);
return users[0].destroy();
})
.then(() => {
return this.ParanoidUser.findAll();
})
.then(users => {
expect(users).to.have.length(0); expect(users).to.have.length(0);
}); });
});
it('sequelize.and as where should include paranoid condition', function() { it('sequelize.and as where should include paranoid condition', async function() {
return this.ParanoidUser.create({ username: 'cuss' }) await this.ParanoidUser.create({ username: 'cuss' });
.then(() => {
return this.ParanoidUser.findAll({ const users0 = await this.ParanoidUser.findAll({
where: this.sequelize.and({ where: this.sequelize.and({
username: 'cuss' username: 'cuss'
}) })
}); });
})
.then(users => { expect(users0).to.have.length(1);
expect(users).to.have.length(1); await users0[0].destroy();
return users[0].destroy();
}) const users = await this.ParanoidUser.findAll({
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.and({ where: this.sequelize.and({
username: 'cuss' username: 'cuss'
}) })
}); });
})
.then(users => {
expect(users).to.have.length(0); expect(users).to.have.length(0);
}); });
});
it('sequelize.or as where should include paranoid condition', function() { it('sequelize.or as where should include paranoid condition', async function() {
return this.ParanoidUser.create({ username: 'cuss' }) await this.ParanoidUser.create({ username: 'cuss' });
.then(() => {
return this.ParanoidUser.findAll({ const users0 = await this.ParanoidUser.findAll({
where: this.sequelize.or({ where: this.sequelize.or({
username: 'cuss' username: 'cuss'
}) })
}); });
})
.then(users => { expect(users0).to.have.length(1);
expect(users).to.have.length(1); await users0[0].destroy();
return users[0].destroy();
}) const users = await this.ParanoidUser.findAll({
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.or({ where: this.sequelize.or({
username: 'cuss' username: 'cuss'
}) })
}); });
})
.then(users => {
expect(users).to.have.length(0); expect(users).to.have.length(0);
}); });
});
it('escapes a single single quotes properly in where clauses', function() { it('escapes a single single quotes properly in where clauses', async function() {
return this.User await this.User
.create({ username: "user'name" }) .create({ username: "user'name" });
.then(() => {
return this.User.findAll({ const users = await this.User.findAll({
where: { username: "user'name" } where: { username: "user'name" }
}).then(users => { });
expect(users.length).to.equal(1); expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user'name"); expect(users[0].username).to.equal("user'name");
}); });
});
});
it('escapes two single quotes properly in where clauses', function() { it('escapes two single quotes properly in where clauses', async function() {
return this.User await this.User
.create({ username: "user''name" }) .create({ username: "user''name" });
.then(() => {
return this.User.findAll({ const users = await this.User.findAll({
where: { username: "user''name" } where: { username: "user''name" }
}).then(users => { });
expect(users.length).to.equal(1); expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user''name"); expect(users[0].username).to.equal("user''name");
}); });
});
});
it('returns the timestamps if no attributes have been specified', function() { it('returns the timestamps if no attributes have been specified', async function() {
return this.User.create({ username: 'fnord' }).then(() => { await this.User.create({ username: 'fnord' });
return this.User.findAll().then(users => { const users = await this.User.findAll();
expect(users[0].createdAt).to.exist; expect(users[0].createdAt).to.exist;
}); });
});
});
it('does not return the timestamps if the username attribute has been specified', function() { it('does not return the timestamps if the username attribute has been specified', async function() {
return this.User.create({ username: 'fnord' }).then(() => { await this.User.create({ username: 'fnord' });
return this.User.findAll({ attributes: ['username'] }).then(users => { const users = await this.User.findAll({ attributes: ['username'] });
expect(users[0].createdAt).not.to.exist; expect(users[0].createdAt).not.to.exist;
expect(users[0].username).to.exist; expect(users[0].username).to.exist;
}); });
});
});
it('creates the deletedAt property, when defining paranoid as true', function() { it('creates the deletedAt property, when defining paranoid as true', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
expect(users[0].deletedAt).to.be.null; expect(users[0].deletedAt).to.be.null;
}); });
});
});
it('destroys a record with a primary key of something other than id', function() { it('destroys a record with a primary key of something other than id', async function() {
const UserDestroy = this.sequelize.define('UserDestroy', { const UserDestroy = this.sequelize.define('UserDestroy', {
newId: { newId: {
type: DataTypes.STRING, type: DataTypes.STRING,
...@@ -436,77 +392,58 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -436,77 +392,58 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
email: DataTypes.STRING email: DataTypes.STRING
}); });
return UserDestroy.sync().then(() => { await UserDestroy.sync();
return UserDestroy.create({ newId: '123ABC', email: 'hello' }).then(() => { await UserDestroy.create({ newId: '123ABC', email: 'hello' });
return UserDestroy.findOne({ where: { email: 'hello' } }).then(user => { const user = await UserDestroy.findOne({ where: { email: 'hello' } });
return user.destroy();
}); await user.destroy();
});
});
}); });
it('sets deletedAt property to a specific date when deleting an instance', function() { it('sets deletedAt property to a specific date when deleting an instance', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
return users[0].destroy().then(() => { await users[0].destroy();
expect(users[0].deletedAt.getMonth).to.exist; expect(users[0].deletedAt.getMonth).to.exist;
return users[0].reload({ paranoid: false }).then(user => { const user = await users[0].reload({ paranoid: false });
expect(user.deletedAt.getMonth).to.exist; expect(user.deletedAt.getMonth).to.exist;
}); });
});
});
});
});
it('keeps the deletedAt-attribute with value null, when running update', function() { it('keeps the deletedAt-attribute with value null, when running update', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
return users[0].update({ username: 'newFnord' }).then(user => { const user = await users[0].update({ username: 'newFnord' });
expect(user.deletedAt).not.to.exist; expect(user.deletedAt).not.to.exist;
}); });
});
});
});
it('keeps the deletedAt-attribute with value null, when updating associations', function() { it('keeps the deletedAt-attribute with value null, when updating associations', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
return this.ParanoidUser.create({ username: 'linkedFnord' }).then(linkedUser => { const linkedUser = await this.ParanoidUser.create({ username: 'linkedFnord' });
return users[0].setParanoidUser(linkedUser).then(user => { const user = await users[0].setParanoidUser(linkedUser);
expect(user.deletedAt).not.to.exist; expect(user.deletedAt).not.to.exist;
}); });
});
});
});
});
it('can reuse query option objects', function() { it('can reuse query option objects', async function() {
return this.User.create({ username: 'fnord' }).then(() => { await this.User.create({ username: 'fnord' });
const query = { where: { username: 'fnord' } }; const query = { where: { username: 'fnord' } };
return this.User.findAll(query).then(users => { const users = await this.User.findAll(query);
expect(users[0].username).to.equal('fnord');
return this.User.findAll(query).then(users => {
expect(users[0].username).to.equal('fnord'); expect(users[0].username).to.equal('fnord');
}); const users0 = await this.User.findAll(query);
}); expect(users0[0].username).to.equal('fnord');
});
}); });
}); });
describe('findOne', () => { describe('findOne', () => {
it('can reuse query option objects', function() { it('can reuse query option objects', async function() {
return this.User.create({ username: 'fnord' }).then(() => { await this.User.create({ username: 'fnord' });
const query = { where: { username: 'fnord' } }; const query = { where: { username: 'fnord' } };
return this.User.findOne(query).then(user => { const user = await this.User.findOne(query);
expect(user.username).to.equal('fnord');
return this.User.findOne(query).then(user => {
expect(user.username).to.equal('fnord'); expect(user.username).to.equal('fnord');
const user0 = await this.User.findOne(query);
expect(user0.username).to.equal('fnord');
}); });
}); it('returns null for null, undefined, and unset boolean values', async function() {
});
});
it('returns null for null, undefined, and unset boolean values', function() {
const Setting = this.sequelize.define('SettingHelper', { const Setting = this.sequelize.define('SettingHelper', {
setting_key: DataTypes.STRING, setting_key: DataTypes.STRING,
bool_value: { type: DataTypes.BOOLEAN, allowNull: true }, bool_value: { type: DataTypes.BOOLEAN, allowNull: true },
...@@ -514,28 +451,23 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -514,28 +451,23 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
bool_value3: { type: DataTypes.BOOLEAN, allowNull: true } bool_value3: { type: DataTypes.BOOLEAN, allowNull: true }
}, { timestamps: false, logging: false }); }, { timestamps: false, logging: false });
return Setting.sync({ force: true }).then(() => { await Setting.sync({ force: true });
return Setting.create({ setting_key: 'test', bool_value: null, bool_value2: undefined }).then(() => { await Setting.create({ setting_key: 'test', bool_value: null, bool_value2: undefined });
return Setting.findOne({ where: { setting_key: 'test' } }).then(setting => { const setting = await Setting.findOne({ where: { setting_key: 'test' } });
expect(setting.bool_value).to.equal(null); expect(setting.bool_value).to.equal(null);
expect(setting.bool_value2).to.equal(null); expect(setting.bool_value2).to.equal(null);
expect(setting.bool_value3).to.equal(null); expect(setting.bool_value3).to.equal(null);
}); });
}); });
});
});
});
describe('equals', () => { describe('equals', () => {
it('can compare records with Date field', function() { it('can compare records with Date field', async function() {
return this.User.create({ username: 'fnord' }).then(user1 => { const user1 = await this.User.create({ username: 'fnord' });
return this.User.findOne({ where: { username: 'fnord' } }).then(user2 => { const user2 = await this.User.findOne({ where: { username: 'fnord' } });
expect(user1.equals(user2)).to.be.true; expect(user1.equals(user2)).to.be.true;
}); });
});
});
it('does not compare the existence of associations', function() { it('does not compare the existence of associations', async function() {
this.UserAssociationEqual = this.sequelize.define('UserAssociationEquals', { this.UserAssociationEqual = this.sequelize.define('UserAssociationEquals', {
username: DataTypes.STRING, username: DataTypes.STRING,
age: DataTypes.INTEGER age: DataTypes.INTEGER
...@@ -549,13 +481,13 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -549,13 +481,13 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
this.UserAssociationEqual.hasMany(this.ProjectAssociationEqual, { as: 'Projects', foreignKey: 'userId' }); this.UserAssociationEqual.hasMany(this.ProjectAssociationEqual, { as: 'Projects', foreignKey: 'userId' });
this.ProjectAssociationEqual.belongsTo(this.UserAssociationEqual, { as: 'Users', foreignKey: 'userId' }); this.ProjectAssociationEqual.belongsTo(this.UserAssociationEqual, { as: 'Users', foreignKey: 'userId' });
return this.UserAssociationEqual.sync({ force: true }).then(() => { await this.UserAssociationEqual.sync({ force: true });
return this.ProjectAssociationEqual.sync({ force: true }).then(() => { await this.ProjectAssociationEqual.sync({ force: true });
return this.UserAssociationEqual.create({ username: 'jimhalpert' }).then(user1 => { const user1 = await this.UserAssociationEqual.create({ username: 'jimhalpert' });
return this.ProjectAssociationEqual.create({ title: 'A Cool Project' }).then(project1 => { const project1 = await this.ProjectAssociationEqual.create({ title: 'A Cool Project' });
return user1.setProjects([project1]).then(() => { await user1.setProjects([project1]);
return this.UserAssociationEqual.findOne({ where: { username: 'jimhalpert' }, include: [{ model: this.ProjectAssociationEqual, as: 'Projects' }] }).then(user2 => { const user2 = await this.UserAssociationEqual.findOne({ where: { username: 'jimhalpert' }, include: [{ model: this.ProjectAssociationEqual, as: 'Projects' }] });
return this.UserAssociationEqual.create({ username: 'pambeesly' }).then(user3 => { const user3 = await this.UserAssociationEqual.create({ username: 'pambeesly' });
expect(user1.get('Projects')).to.not.exist; expect(user1.get('Projects')).to.not.exist;
expect(user2.get('Projects')).to.exist; expect(user2.get('Projects')).to.exist;
expect(user1.equals(user2)).to.be.true; expect(user1.equals(user2)).to.be.true;
...@@ -564,65 +496,50 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -564,65 +496,50 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
expect(user3.equals(user1)).to.not.be.true; expect(user3.equals(user1)).to.not.be.true;
}); });
}); });
});
});
});
});
});
});
});
describe('values', () => { describe('values', () => {
it('returns all values', function() { it('returns all values', async function() {
const User = this.sequelize.define('UserHelper', { const User = this.sequelize.define('UserHelper', {
username: DataTypes.STRING username: DataTypes.STRING
}, { timestamps: false, logging: false }); }, { timestamps: false, logging: false });
return User.sync().then(() => { await User.sync();
const user = User.build({ username: 'foo' }); const user = User.build({ username: 'foo' });
expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null }); expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null });
}); });
}); });
});
describe('isSoftDeleted', () => { describe('isSoftDeleted', () => {
beforeEach(function() { beforeEach(async function() {
this.ParanoidUser = this.sequelize.define('ParanoidUser', { this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING } username: { type: DataTypes.STRING }
}, { paranoid: true }); }, { paranoid: true });
return this.ParanoidUser.sync({ force: true }); await this.ParanoidUser.sync({ force: true });
}); });
it('should return false when model is just created', function() { it('should return false when model is just created', async function() {
return this.ParanoidUser.create({ username: 'foo' }).then(user => { const user = await this.ParanoidUser.create({ username: 'foo' });
expect(user.isSoftDeleted()).to.be.false; expect(user.isSoftDeleted()).to.be.false;
}); });
});
it('returns false if user is not soft deleted', function() { it('returns false if user is not soft deleted', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
expect(users[0].isSoftDeleted()).to.be.false; expect(users[0].isSoftDeleted()).to.be.false;
}); });
});
});
it('returns true if user is soft deleted', function() { it('returns true if user is soft deleted', async function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => { await this.ParanoidUser.create({ username: 'fnord' });
return this.ParanoidUser.findAll().then(users => { const users = await this.ParanoidUser.findAll();
return users[0].destroy().then(() => { await users[0].destroy();
expect(users[0].isSoftDeleted()).to.be.true; expect(users[0].isSoftDeleted()).to.be.true;
return users[0].reload({ paranoid: false }).then(user => { const user = await users[0].reload({ paranoid: false });
expect(user.isSoftDeleted()).to.be.true; expect(user.isSoftDeleted()).to.be.true;
}); });
});
});
});
});
it('works with custom `deletedAt` field name', function() { it('works with custom `deletedAt` field name', async function() {
this.ParanoidUserWithCustomDeletedAt = this.sequelize.define('ParanoidUserWithCustomDeletedAt', { this.ParanoidUserWithCustomDeletedAt = this.sequelize.define('ParanoidUserWithCustomDeletedAt', {
username: { type: DataTypes.STRING } username: { type: DataTypes.STRING }
}, { }, {
...@@ -632,23 +549,18 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -632,23 +549,18 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
this.ParanoidUserWithCustomDeletedAt.hasOne(this.ParanoidUser); this.ParanoidUserWithCustomDeletedAt.hasOne(this.ParanoidUser);
return this.ParanoidUserWithCustomDeletedAt.sync({ force: true }).then(() => { await this.ParanoidUserWithCustomDeletedAt.sync({ force: true });
return this.ParanoidUserWithCustomDeletedAt.create({ username: 'fnord' }).then(() => { await this.ParanoidUserWithCustomDeletedAt.create({ username: 'fnord' });
return this.ParanoidUserWithCustomDeletedAt.findAll().then(users => { const users = await this.ParanoidUserWithCustomDeletedAt.findAll();
expect(users[0].isSoftDeleted()).to.be.false; expect(users[0].isSoftDeleted()).to.be.false;
return users[0].destroy().then(() => { await users[0].destroy();
expect(users[0].isSoftDeleted()).to.be.true; expect(users[0].isSoftDeleted()).to.be.true;
return users[0].reload({ paranoid: false }).then(user => { const user = await users[0].reload({ paranoid: false });
expect(user.isSoftDeleted()).to.be.true; expect(user.isSoftDeleted()).to.be.true;
}); });
}); });
});
});
});
});
});
describe('restore', () => { describe('restore', () => {
it('returns an error if the model is not paranoid', async function() { it('returns an error if the model is not paranoid', async function() {
...@@ -656,7 +568,7 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -656,7 +568,7 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
await expect(user.restore()).to.be.rejectedWith(Error, 'Model is not paranoid'); await expect(user.restore()).to.be.rejectedWith(Error, 'Model is not paranoid');
}); });
it('restores a previously deleted model', function() { it('restores a previously deleted model', async function() {
const ParanoidUser = this.sequelize.define('ParanoidUser', { const ParanoidUser = this.sequelize.define('ParanoidUser', {
username: DataTypes.STRING, username: DataTypes.STRING,
secretValue: DataTypes.STRING, secretValue: DataTypes.STRING,
...@@ -669,124 +581,106 @@ describe(Support.getTestDialectTeaser('Instance'), () => { ...@@ -669,124 +581,106 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
{ username: 'Paul', secretValue: '43' }, { username: 'Paul', secretValue: '43' },
{ username: 'Bob', secretValue: '44' }]; { username: 'Bob', secretValue: '44' }];
return ParanoidUser.sync({ force: true }).then(() => { await ParanoidUser.sync({ force: true });
return ParanoidUser.bulkCreate(data); await ParanoidUser.bulkCreate(data);
}).then(() => { const user0 = await ParanoidUser.findOne({ where: { secretValue: '42' } });
return ParanoidUser.findOne({ where: { secretValue: '42' } }); await user0.destroy();
}).then(user => { await user0.restore();
return user.destroy().then(() => { const user = await ParanoidUser.findOne({ where: { secretValue: '42' } });
return user.restore();
});
}).then(() => {
return ParanoidUser.findOne({ where: { secretValue: '42' } });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.username).to.equal('Peter'); expect(user.username).to.equal('Peter');
}); });
});
it('supports custom deletedAt field', function() { it('supports custom deletedAt field', async function() {
const ParanoidUser = this.sequelize.define('ParanoidUser', { const ParanoidUser = this.sequelize.define('ParanoidUser', {
username: DataTypes.STRING, username: DataTypes.STRING,
destroyTime: DataTypes.DATE destroyTime: DataTypes.DATE
}, { paranoid: true, deletedAt: 'destroyTime' }); }, { paranoid: true, deletedAt: 'destroyTime' });
return ParanoidUser.sync({ force: true }).then(() => { await ParanoidUser.sync({ force: true });
return ParanoidUser.create({
const user2 = await ParanoidUser.create({
username: 'username' username: 'username'
}); });
}).then(user => {
return user.destroy(); const user1 = await user2.destroy();
}).then(user => { expect(user1.destroyTime).to.be.ok;
expect(user.destroyTime).to.be.ok; expect(user1.deletedAt).to.not.be.ok;
expect(user.deletedAt).to.not.be.ok; const user0 = await user1.restore();
return user.restore(); expect(user0.destroyTime).to.not.be.ok;
}).then(user => { const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user.destroyTime).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.destroyTime).to.not.be.ok; expect(user.destroyTime).to.not.be.ok;
expect(user.deletedAt).to.not.be.ok; expect(user.deletedAt).to.not.be.ok;
}); });
});
it('supports custom deletedAt field name', function() { it('supports custom deletedAt field name', async function() {
const ParanoidUser = this.sequelize.define('ParanoidUser', { const ParanoidUser = this.sequelize.define('ParanoidUser', {
username: DataTypes.STRING, username: DataTypes.STRING,
deletedAt: { type: DataTypes.DATE, field: 'deleted_at' } deletedAt: { type: DataTypes.DATE, field: 'deleted_at' }
}, { paranoid: true }); }, { paranoid: true });
return ParanoidUser.sync({ force: true }).then(() => { await ParanoidUser.sync({ force: true });
return ParanoidUser.create({
const user2 = await ParanoidUser.create({
username: 'username' username: 'username'
}); });
}).then(user => {
return user.destroy(); const user1 = await user2.destroy();
}).then(user => { expect(user1.dataValues.deletedAt).to.be.ok;
expect(user.dataValues.deletedAt).to.be.ok; expect(user1.dataValues.deleted_at).to.not.be.ok;
expect(user.dataValues.deleted_at).to.not.be.ok; const user0 = await user1.restore();
return user.restore(); expect(user0.dataValues.deletedAt).to.not.be.ok;
}).then(user => { expect(user0.dataValues.deleted_at).to.not.be.ok;
expect(user.dataValues.deletedAt).to.not.be.ok; const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user.dataValues.deleted_at).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.deletedAt).to.not.be.ok; expect(user.deletedAt).to.not.be.ok;
expect(user.deleted_at).to.not.be.ok; expect(user.deleted_at).to.not.be.ok;
}); });
});
it('supports custom deletedAt field and database column', function() { it('supports custom deletedAt field and database column', async function() {
const ParanoidUser = this.sequelize.define('ParanoidUser', { const ParanoidUser = this.sequelize.define('ParanoidUser', {
username: DataTypes.STRING, username: DataTypes.STRING,
destroyTime: { type: DataTypes.DATE, field: 'destroy_time' } destroyTime: { type: DataTypes.DATE, field: 'destroy_time' }
}, { paranoid: true, deletedAt: 'destroyTime' }); }, { paranoid: true, deletedAt: 'destroyTime' });
return ParanoidUser.sync({ force: true }).then(() => { await ParanoidUser.sync({ force: true });
return ParanoidUser.create({
const user2 = await ParanoidUser.create({
username: 'username' username: 'username'
}); });
}).then(user => {
return user.destroy(); const user1 = await user2.destroy();
}).then(user => { expect(user1.dataValues.destroyTime).to.be.ok;
expect(user.dataValues.destroyTime).to.be.ok; expect(user1.dataValues.deletedAt).to.not.be.ok;
expect(user.dataValues.deletedAt).to.not.be.ok; expect(user1.dataValues.destroy_time).to.not.be.ok;
expect(user.dataValues.destroy_time).to.not.be.ok; const user0 = await user1.restore();
return user.restore(); expect(user0.dataValues.destroyTime).to.not.be.ok;
}).then(user => { expect(user0.dataValues.destroy_time).to.not.be.ok;
expect(user.dataValues.destroyTime).to.not.be.ok; const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user.dataValues.destroy_time).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.destroyTime).to.not.be.ok; expect(user.destroyTime).to.not.be.ok;
expect(user.destroy_time).to.not.be.ok; expect(user.destroy_time).to.not.be.ok;
}); });
});
it('supports custom default value', function() { it('supports custom default value', async function() {
const ParanoidUser = this.sequelize.define('ParanoidUser', { const ParanoidUser = this.sequelize.define('ParanoidUser', {
username: DataTypes.STRING, username: DataTypes.STRING,
deletedAt: { type: DataTypes.DATE, defaultValue: new Date(0) } deletedAt: { type: DataTypes.DATE, defaultValue: new Date(0) }
}, { paranoid: true }); }, { paranoid: true });
return ParanoidUser.sync({ force: true }).then(() => { await ParanoidUser.sync({ force: true });
return ParanoidUser.create({
const user2 = await ParanoidUser.create({
username: 'username' username: 'username'
}); });
}).then(user => {
return user.destroy(); const user1 = await user2.destroy();
}).then(user => { const user0 = await user1.restore();
return user.restore(); expect(user0.dataValues.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
}).then(user => { const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user.dataValues.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.deletedAt.toISOString()).to.equal(new Date(0).toISOString()); expect(user.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
}); });
}); });
});
}); });
...@@ -8,7 +8,7 @@ const chai = require('chai'), ...@@ -8,7 +8,7 @@ const chai = require('chai'),
describe(Support.getTestDialectTeaser('InstanceValidator'), () => { describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
describe('#update', () => { describe('#update', () => {
it('should allow us to update specific columns without tripping the validations', function() { it('should allow us to update specific columns without tripping the validations', async function() {
const User = this.sequelize.define('model', { const User = this.sequelize.define('model', {
username: Sequelize.STRING, username: Sequelize.STRING,
email: { email: {
...@@ -22,20 +22,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -22,20 +22,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ username: 'bob', email: 'hello@world.com' }).then(user => { const user = await User.create({ username: 'bob', email: 'hello@world.com' });
return User
.update({ username: 'toni' }, { where: { id: user.id } }) await User
.then(() => { .update({ username: 'toni' }, { where: { id: user.id } });
return User.findByPk(1).then(user => {
expect(user.username).to.equal('toni'); const user0 = await User.findByPk(1);
}); expect(user0.username).to.equal('toni');
});
});
});
}); });
it('should be able to emit an error upon updating when a validation has failed from an instance', function() { it('should be able to emit an error upon updating when a validation has failed from an instance', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -46,17 +43,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -46,17 +43,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create({ name: 'World' }).then(model => { const model = await Model.create({ name: 'World' });
return model.update({ name: '' }).catch(err => {
try {
await model.update({ name: '' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed'); expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed');
}); }
});
});
}); });
it('should be able to emit an error upon updating when a validation has failed from the factory', function() { it('should be able to emit an error upon updating when a validation has failed from the factory', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -67,17 +65,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -67,17 +65,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return Model.sync({ force: true }).then(() => { await Model.sync({ force: true });
return Model.create({ name: 'World' }).then(() => { await Model.create({ name: 'World' });
return Model.update({ name: '' }, { where: { id: 1 } }).catch(err => {
try {
await Model.update({ name: '' }, { where: { id: 1 } });
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed'); expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed');
}); }
});
});
}); });
it('should enforce a unique constraint', function() { it('should enforce a unique constraint', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
uniqueName: { type: Sequelize.STRING, unique: 'uniqueName' } uniqueName: { type: Sequelize.STRING, unique: 'uniqueName' }
}); });
...@@ -85,24 +84,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -85,24 +84,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName: 'unique name one' }, { uniqueName: 'unique name one' },
{ uniqueName: 'unique name two' } { uniqueName: 'unique name two' }
]; ];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => { const instance0 = await Model.create(records[0]);
return Model.create(records[0]); expect(instance0).to.be.ok;
}).then(instance => { const instance = await Model.create(records[1]);
expect(instance).to.be.ok;
return Model.create(records[1]);
}).then(instance => {
expect(instance).to.be.ok; expect(instance).to.be.ok;
return expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected; const err = await expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1); expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName'); expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.include('must be unique'); expect(err.errors[0].message).to.include('must be unique');
}); });
});
it('should allow a custom unique constraint error message', function() { it('should allow a custom unique constraint error message', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
uniqueName: { uniqueName: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -113,24 +107,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -113,24 +107,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName: 'unique name one' }, { uniqueName: 'unique name one' },
{ uniqueName: 'unique name two' } { uniqueName: 'unique name two' }
]; ];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => { const instance0 = await Model.create(records[0]);
return Model.create(records[0]); expect(instance0).to.be.ok;
}).then(instance => { const instance = await Model.create(records[1]);
expect(instance).to.be.ok;
return Model.create(records[1]);
}).then(instance => {
expect(instance).to.be.ok; expect(instance).to.be.ok;
return expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected; const err = await expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1); expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName'); expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.equal('custom unique error message'); expect(err.errors[0].message).to.equal('custom unique error message');
}); });
});
it('should handle multiple unique messages correctly', function() { it('should handle multiple unique messages correctly', async function() {
const Model = this.sequelize.define('model', { const Model = this.sequelize.define('model', {
uniqueName1: { uniqueName1: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -146,31 +135,26 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -146,31 +135,26 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName1: 'unique name one', uniqueName2: 'this is ok' }, { uniqueName1: 'unique name one', uniqueName2: 'this is ok' },
{ uniqueName1: 'this is ok', uniqueName2: 'unique name one' } { uniqueName1: 'this is ok', uniqueName2: 'unique name one' }
]; ];
return Model.sync({ force: true }) await Model.sync({ force: true });
.then(() => { const instance = await Model.create(records[0]);
return Model.create(records[0]);
}).then(instance => {
expect(instance).to.be.ok; expect(instance).to.be.ok;
return expect(Model.create(records[1])).to.be.rejected; const err0 = await expect(Model.create(records[1])).to.be.rejected;
}).then(err => { expect(err0).to.be.an.instanceOf(Error);
expect(err).to.be.an.instanceOf(Error); expect(err0.errors).to.have.length(1);
expect(err.errors).to.have.length(1); expect(err0.errors[0].path).to.include('uniqueName1');
expect(err.errors[0].path).to.include('uniqueName1'); expect(err0.errors[0].message).to.equal('custom unique error message 1');
expect(err.errors[0].message).to.equal('custom unique error message 1');
return expect(Model.create(records[2])).to.be.rejected; const err = await expect(Model.create(records[2])).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1); expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName2'); expect(err.errors[0].path).to.include('uniqueName2');
expect(err.errors[0].message).to.equal('custom unique error message 2'); expect(err.errors[0].message).to.equal('custom unique error message 2');
}); });
}); });
});
describe('#create', () => { describe('#create', () => {
describe('generic', () => { describe('generic', () => {
beforeEach(function() { beforeEach(async function() {
const Project = this.sequelize.define('Project', { const Project = this.sequelize.define('Project', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -189,34 +173,31 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -189,34 +173,31 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
Project.hasOne(Task); Project.hasOne(Task);
Task.belongsTo(Project); Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
this.Project = Project; this.Project = Project;
this.Task = Task; this.Task = Task;
}); });
});
it('correctly throws an error using create method ', function() { it('correctly throws an error using create method ', async function() {
return this.Project.create({ name: 'nope' }).catch(err => { try {
await this.Project.create({ name: 'nope' });
} catch (err) {
expect(err).to.have.ownProperty('name'); expect(err).to.have.ownProperty('name');
}); }
}); });
it('correctly validates using create method ', function() { it('correctly validates using create method ', async function() {
return this.Project.create({}).then(project => { const project = await this.Project.create({});
return this.Task.create({ something: 1 }).then(task => { const task = await this.Task.create({ something: 1 });
return project.setTask(task).then(task => { const task0 = await project.setTask(task);
expect(task.ProjectId).to.not.be.null; expect(task0.ProjectId).to.not.be.null;
return task.setProject(project).then(project => { const project0 = await task0.setProject(project);
expect(project.ProjectId).to.not.be.null; expect(project0.ProjectId).to.not.be.null;
});
});
});
});
}); });
}); });
describe('explicitly validating primary/auto incremented columns', () => { describe('explicitly validating primary/auto incremented columns', () => {
it('should emit an error when we try to enter in a string for the id key without validation arguments', function() { it('should emit an error when we try to enter in a string for the id key without validation arguments', async function() {
const User = this.sequelize.define('UserId', { const User = this.sequelize.define('UserId', {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -228,15 +209,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -228,15 +209,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ id: 'helloworld' }).catch(err => {
try {
await User.create({ id: 'helloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('id')[0].message).to.equal('Validation isInt on id failed'); expect(err.get('id')[0].message).to.equal('Validation isInt on id failed');
}); }
});
}); });
it('should emit an error when we try to enter in a string for an auto increment key (not named id)', function() { it('should emit an error when we try to enter in a string for an auto increment key (not named id)', async function() {
const User = this.sequelize.define('UserId', { const User = this.sequelize.define('UserId', {
username: { username: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -248,16 +231,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -248,16 +231,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ username: 'helloworldhelloworld' }).catch(err => {
try {
await User.create({ username: 'helloworldhelloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('username')[0].message).to.equal('Username must be an integer!'); expect(err.get('username')[0].message).to.equal('Username must be an integer!');
}); }
});
}); });
describe('primaryKey with the name as id with arguments for it\'s validatio', () => { describe('primaryKey with the name as id with arguments for it\'s validatio', () => {
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('UserId', { this.User = this.sequelize.define('UserId', {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -269,36 +254,40 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -269,36 +254,40 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return this.User.sync({ force: true }); await this.User.sync({ force: true });
}); });
it('should emit an error when we try to enter in a string for the id key with validation arguments', function() { it('should emit an error when we try to enter in a string for the id key with validation arguments', async function() {
return this.User.create({ id: 'helloworld' }).catch(err => { try {
await this.User.create({ id: 'helloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('id')[0].message).to.equal('ID must be an integer!'); expect(err.get('id')[0].message).to.equal('ID must be an integer!');
}); }
}); });
it('should emit an error when we try to enter in a string for an auto increment key through .build().validate()', function() { it('should emit an error when we try to enter in a string for an auto increment key through .build().validate()', async function() {
const user = this.User.build({ id: 'helloworld' }); const user = this.User.build({ id: 'helloworld' });
return expect(user.validate()).to.be.rejected.then(err => { const err = await expect(user.validate()).to.be.rejected;
expect(err.get('id')[0].message).to.equal('ID must be an integer!'); expect(err.get('id')[0].message).to.equal('ID must be an integer!');
}); });
});
it('should emit an error when we try to .save()', function() { it('should emit an error when we try to .save()', async function() {
const user = this.User.build({ id: 'helloworld' }); const user = this.User.build({ id: 'helloworld' });
return user.save().catch(err => {
try {
await user.save();
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
expect(err.get('id')[0].message).to.equal('ID must be an integer!'); expect(err.get('id')[0].message).to.equal('ID must be an integer!');
}); }
}); });
}); });
}); });
describe('pass all paths when validating', () => { describe('pass all paths when validating', () => {
beforeEach(function() { beforeEach(async function() {
const Project = this.sequelize.define('Project', { const Project = this.sequelize.define('Project', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -325,25 +314,25 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -325,25 +314,25 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
Project.hasOne(Task); Project.hasOne(Task);
Task.belongsTo(Project); Task.belongsTo(Project);
return Project.sync({ force: true }).then(() => { await Project.sync({ force: true });
return Task.sync({ force: true }).then(() => { await Task.sync({ force: true });
this.Project = Project; this.Project = Project;
this.Task = Task; this.Task = Task;
}); });
});
});
it('produce 3 errors', function() { it('produce 3 errors', async function() {
return this.Project.create({}).catch(err => { try {
await this.Project.create({});
} catch (err) {
expect(err).to.be.an.instanceOf(Error); expect(err).to.be.an.instanceOf(Error);
delete err.stack; // longStackTraces delete err.stack; // longStackTraces
expect(err.errors).to.have.length(3); expect(err.errors).to.have.length(3);
}); }
}); });
}); });
describe('not null schema validation', () => { describe('not null schema validation', () => {
beforeEach(function() { beforeEach(async function() {
const Project = this.sequelize.define('Project', { const Project = this.sequelize.define('Project', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -354,13 +343,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -354,13 +343,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
this.Project = Project; this.Project = Project;
}); });
});
it('correctly throws an error using create method ', function() { it('correctly throws an error using create method ', async function() {
return this.Project.create({}) await this.Project.create({})
.then(() => { .then(() => {
throw new Error('Validation must be failed'); throw new Error('Validation must be failed');
}, () => { }, () => {
...@@ -368,18 +356,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -368,18 +356,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}); });
}); });
it('correctly throws an error using create method with default generated messages', function() { it('correctly throws an error using create method with default generated messages', async function() {
return this.Project.create({}).catch(err => { try {
await this.Project.create({});
} catch (err) {
expect(err).to.have.property('name', 'SequelizeValidationError'); expect(err).to.have.property('name', 'SequelizeValidationError');
expect(err.message).equal('notNull Violation: Project.name cannot be null'); expect(err.message).equal('notNull Violation: Project.name cannot be null');
expect(err.errors).to.be.an('array').and.have.length(1); expect(err.errors).to.be.an('array').and.have.length(1);
expect(err.errors[0]).to.have.property('message', 'Project.name cannot be null'); expect(err.errors[0]).to.have.property('message', 'Project.name cannot be null');
}); }
}); });
}); });
}); });
it('correctly validates using custom validation methods', function() { it('correctly validates using custom validation methods', async function() {
const User = this.sequelize.define(`User${config.rand()}`, { const User = this.sequelize.define(`User${config.rand()}`, {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -397,43 +387,39 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -397,43 +387,39 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingUser = User.build({ name: '3' }); const failingUser = User.build({ name: '3' });
return expect(failingUser.validate()).to.be.rejected.then(error => { const error = await expect(failingUser.validate()).to.be.rejected;
expect(error).to.be.an.instanceOf(Error); expect(error).to.be.an.instanceOf(Error);
expect(error.get('name')[0].message).to.equal("name should equal '2'"); expect(error.get('name')[0].message).to.equal("name should equal '2'");
const successfulUser = User.build({ name: '2' }); const successfulUser = User.build({ name: '2' });
return expect(successfulUser.validate()).not.to.be.rejected;
}); await expect(successfulUser.validate()).not.to.be.rejected;
}); });
it('supports promises with custom validation methods', function() { it('supports promises with custom validation methods', async function() {
const User = this.sequelize.define(`User${config.rand()}`, { const User = this.sequelize.define(`User${config.rand()}`, {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
validate: { validate: {
customFn(val) { async customFn(val) {
return User.findAll() await User.findAll();
.then(() => {
if (val === 'error') { if (val === 'error') {
throw new Error('Invalid username'); throw new Error('Invalid username');
} }
});
} }
} }
} }
}); });
return User.sync().then(() => { await User.sync();
return expect(User.build({ name: 'error' }).validate()).to.be.rejected.then(error => { const error = await expect(User.build({ name: 'error' }).validate()).to.be.rejected;
expect(error).to.be.instanceof(Sequelize.ValidationError); expect(error).to.be.instanceof(Sequelize.ValidationError);
expect(error.get('name')[0].message).to.equal('Invalid username'); expect(error.get('name')[0].message).to.equal('Invalid username');
return expect(User.build({ name: 'no error' }).validate()).not.to.be.rejected; await expect(User.build({ name: 'no error' }).validate()).not.to.be.rejected;
});
});
}); });
it('skips other validations if allowNull is true and the value is null', function() { it('skips other validations if allowNull is true and the value is null', async function() {
const User = this.sequelize.define(`User${config.rand()}`, { const User = this.sequelize.define(`User${config.rand()}`, {
age: { age: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -444,16 +430,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -444,16 +430,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return expect(User const error = await expect(User
.build({ age: -1 }) .build({ age: -1 })
.validate()) .validate())
.to.be.rejected .to.be.rejected;
.then(error => {
expect(error.get('age')[0].message).to.equal('must be positive'); expect(error.get('age')[0].message).to.equal('must be positive');
}); });
});
it('validates a model with custom model-wide validation methods', function() { it('validates a model with custom model-wide validation methods', async function() {
const Foo = this.sequelize.define(`Foo${config.rand()}`, { const Foo = this.sequelize.define(`Foo${config.rand()}`, {
field1: { field1: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -473,21 +458,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -473,21 +458,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return expect(Foo const error = await expect(Foo
.build({ field1: null, field2: null }) .build({ field1: null, field2: null })
.validate()) .validate())
.to.be.rejected .to.be.rejected;
.then(error => {
expect(error.get('xnor')[0].message).to.equal('xnor failed'); expect(error.get('xnor')[0].message).to.equal('xnor failed');
return expect(Foo await expect(Foo
.build({ field1: 33, field2: null }) .build({ field1: 33, field2: null })
.validate()) .validate())
.not.to.be.rejected; .not.to.be.rejected;
}); });
});
it('validates model with a validator whose arg is an Array successfully twice in a row', function() { it('validates model with a validator whose arg is an Array successfully twice in a row', async function() {
const Foo = this.sequelize.define(`Foo${config.rand()}`, { const Foo = this.sequelize.define(`Foo${config.rand()}`, {
bar: { bar: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -497,12 +481,11 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -497,12 +481,11 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}), }),
foo = Foo.build({ bar: 'a' }); foo = Foo.build({ bar: 'a' });
return expect(foo.validate()).not.to.be.rejected.then(() => { await expect(foo.validate()).not.to.be.rejected;
return expect(foo.validate()).not.to.be.rejected; await expect(foo.validate()).not.to.be.rejected;
});
}); });
it('validates enums', function() { it('validates enums', async function() {
const values = ['value1', 'value2']; const values = ['value1', 'value2'];
const Bar = this.sequelize.define(`Bar${config.rand()}`, { const Bar = this.sequelize.define(`Bar${config.rand()}`, {
...@@ -517,13 +500,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -517,13 +500,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingBar = Bar.build({ field: 'value3' }); const failingBar = Bar.build({ field: 'value3' });
return expect(failingBar.validate()).to.be.rejected.then(errors => { const errors = await expect(failingBar.validate()).to.be.rejected;
expect(errors.get('field')).to.have.length(1); expect(errors.get('field')).to.have.length(1);
expect(errors.get('field')[0].message).to.equal('Validation isIn on field failed'); expect(errors.get('field')[0].message).to.equal('Validation isIn on field failed');
}); });
});
it('skips validations for the given fields', function() { it('skips validations for the given fields', async function() {
const values = ['value1', 'value2']; const values = ['value1', 'value2'];
const Bar = this.sequelize.define(`Bar${config.rand()}`, { const Bar = this.sequelize.define(`Bar${config.rand()}`, {
...@@ -538,10 +520,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -538,10 +520,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingBar = Bar.build({ field: 'value3' }); const failingBar = Bar.build({ field: 'value3' });
return expect(failingBar.validate({ skip: ['field'] })).not.to.be.rejected; await expect(failingBar.validate({ skip: ['field'] })).not.to.be.rejected;
}); });
it('skips validations for fields with value that is SequelizeMethod', function() { it('skips validations for fields with value that is SequelizeMethod', async function() {
const values = ['value1', 'value2']; const values = ['value1', 'value2'];
const Bar = this.sequelize.define(`Bar${config.rand()}`, { const Bar = this.sequelize.define(`Bar${config.rand()}`, {
...@@ -556,10 +538,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -556,10 +538,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingBar = Bar.build({ field: this.sequelize.literal('5 + 1') }); const failingBar = Bar.build({ field: this.sequelize.literal('5 + 1') });
return expect(failingBar.validate()).not.to.be.rejected; await expect(failingBar.validate()).not.to.be.rejected;
}); });
it('raises an error if saving a different value into an immutable field', function() { it('raises an error if saving a different value into an immutable field', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -569,18 +551,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -569,18 +551,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.create({ name: 'RedCat' }).then(user => { const user = await User.create({ name: 'RedCat' });
expect(user.getDataValue('name')).to.equal('RedCat'); expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat'); user.setDataValue('name', 'YellowCat');
return expect(user.save()).to.be.rejected.then(errors => { const errors = await expect(user.save()).to.be.rejected;
expect(errors.get('name')[0].message).to.eql('Validation isImmutable on name failed'); expect(errors.get('name')[0].message).to.eql('Validation isImmutable on name failed');
}); });
});
});
});
it('allows setting an immutable field if the record is unsaved', function() { it('allows setting an immutable field if the record is unsaved', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -594,94 +573,94 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -594,94 +573,94 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
expect(user.getDataValue('name')).to.equal('RedCat'); expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat'); user.setDataValue('name', 'YellowCat');
return expect(user.validate()).not.to.be.rejected; await expect(user.validate()).not.to.be.rejected;
}); });
it('raises an error for array on a STRING', function() { it('raises an error for array on a STRING', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.STRING type: Sequelize.STRING
} }
}); });
return expect(User.build({ await expect(User.build({
email: ['iama', 'dummy.com'] email: ['iama', 'dummy.com']
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('raises an error for array on a STRING(20)', function() { it('raises an error for array on a STRING(20)', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.STRING(20) type: Sequelize.STRING(20)
} }
}); });
return expect(User.build({ await expect(User.build({
email: ['iama', 'dummy.com'] email: ['iama', 'dummy.com']
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('raises an error for array on a TEXT', function() { it('raises an error for array on a TEXT', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.TEXT type: Sequelize.TEXT
} }
}); });
return expect(User.build({ await expect(User.build({
email: ['iama', 'dummy.com'] email: ['iama', 'dummy.com']
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('raises an error for {} on a STRING', function() { it('raises an error for {} on a STRING', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.STRING type: Sequelize.STRING
} }
}); });
return expect(User.build({ await expect(User.build({
email: { lol: true } email: { lol: true }
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('raises an error for {} on a STRING(20)', function() { it('raises an error for {} on a STRING(20)', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.STRING(20) type: Sequelize.STRING(20)
} }
}); });
return expect(User.build({ await expect(User.build({
email: { lol: true } email: { lol: true }
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('raises an error for {} on a TEXT', function() { it('raises an error for {} on a TEXT', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.TEXT type: Sequelize.TEXT
} }
}); });
return expect(User.build({ await expect(User.build({
email: { lol: true } email: { lol: true }
}).validate()).to.be.rejectedWith(Sequelize.ValidationError); }).validate()).to.be.rejectedWith(Sequelize.ValidationError);
}); });
it('does not raise an error for null on a STRING (where null is allowed)', function() { it('does not raise an error for null on a STRING (where null is allowed)', async function() {
const User = this.sequelize.define('User', { const User = this.sequelize.define('User', {
'email': { 'email': {
type: Sequelize.STRING type: Sequelize.STRING
} }
}); });
return expect(User.build({ await expect(User.build({
email: null email: null
}).validate()).not.to.be.rejected; }).validate()).not.to.be.rejected;
}); });
it('validates VIRTUAL fields', function() { it('validates VIRTUAL fields', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
password_hash: Sequelize.STRING, password_hash: Sequelize.STRING,
salt: Sequelize.STRING, salt: Sequelize.STRING,
...@@ -701,7 +680,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -701,7 +680,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return Promise.all([ await Promise.all([
expect(User.build({ expect(User.build({
password: 'short', password: 'short',
salt: '42' salt: '42'
...@@ -715,7 +694,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -715,7 +694,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
]); ]);
}); });
it('allows me to add custom validation functions to validator.js', function() { it('allows me to add custom validation functions to validator.js', async function() {
this.sequelize.Validator.extend('isExactly7Characters', val => { this.sequelize.Validator.extend('isExactly7Characters', val => {
return val.length === 7; return val.length === 7;
}); });
...@@ -729,14 +708,14 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => { ...@@ -729,14 +708,14 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
} }
}); });
return expect(User.build({ await expect(User.build({
name: 'abcdefg' name: 'abcdefg'
}).validate()).not.to.be.rejected.then(() => { }).validate()).not.to.be.rejected;
return expect(User.build({
const errors = await expect(User.build({
name: 'a' name: 'a'
}).validate()).to.be.rejected; }).validate()).to.be.rejected;
}).then(errors => {
expect(errors.get('name')[0].message).to.equal('Validation isExactly7Characters on name failed'); expect(errors.get('name')[0].message).to.equal('Validation isExactly7Characters on name failed');
}); });
});
}); });
...@@ -11,7 +11,7 @@ const chai = require('chai'), ...@@ -11,7 +11,7 @@ const chai = require('chai'),
describe('model', () => { describe('model', () => {
if (current.dialect.supports.JSON) { if (current.dialect.supports.JSON) {
describe('json', () => { describe('json', () => {
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
emergency_contact: DataTypes.JSON, emergency_contact: DataTypes.JSON,
...@@ -20,21 +20,19 @@ describe('model', () => { ...@@ -20,21 +20,19 @@ describe('model', () => {
this.Order = this.sequelize.define('Order'); this.Order = this.sequelize.define('Order');
this.Order.belongsTo(this.User); this.Order.belongsTo(this.User);
return this.sequelize.sync({ force: true }); await this.sequelize.sync({ force: true });
}); });
it('should tell me that a column is json', function() { it('should tell me that a column is json', async function() {
return this.sequelize.queryInterface.describeTable('Users') const table = await this.sequelize.queryInterface.describeTable('Users');
.then(table => {
// expected for mariadb 10.4 : https://jira.mariadb.org/browse/MDEV-15558 // expected for mariadb 10.4 : https://jira.mariadb.org/browse/MDEV-15558
if (dialect !== 'mariadb') { if (dialect !== 'mariadb') {
expect(table.emergency_contact.type).to.equal('JSON'); expect(table.emergency_contact.type).to.equal('JSON');
} }
}); });
});
it('should use a placeholder for json with insert', function() { it('should use a placeholder for json with insert', async function() {
return this.User.create({ await this.User.create({
username: 'bob', username: 'bob',
emergency_contact: { name: 'joe', phones: [1337, 42] } emergency_contact: { name: 'joe', phones: [1337, 42] }
}, { }, {
...@@ -49,228 +47,209 @@ describe('model', () => { ...@@ -49,228 +47,209 @@ describe('model', () => {
}); });
}); });
it('should insert json using a custom field name', function() { it('should insert json using a custom field name', async function() {
this.UserFields = this.sequelize.define('UserFields', { this.UserFields = this.sequelize.define('UserFields', {
emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' } emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' }
}); });
return this.UserFields.sync({ force: true }).then(() => { await this.UserFields.sync({ force: true });
return this.UserFields.create({
const user = await this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] } emergencyContact: { name: 'joe', phones: [1337, 42] }
}).then(user => {
expect(user.emergencyContact.name).to.equal('joe');
});
}); });
expect(user.emergencyContact.name).to.equal('joe');
}); });
it('should update json using a custom field name', function() { it('should update json using a custom field name', async function() {
this.UserFields = this.sequelize.define('UserFields', { this.UserFields = this.sequelize.define('UserFields', {
emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' } emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' }
}); });
return this.UserFields.sync({ force: true }).then(() => { await this.UserFields.sync({ force: true });
return this.UserFields.create({
const user0 = await this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] } emergencyContact: { name: 'joe', phones: [1337, 42] }
}).then(user => {
user.emergencyContact = { name: 'larry' };
return user.save();
}).then(user => {
expect(user.emergencyContact.name).to.equal('larry');
});
}); });
user0.emergencyContact = { name: 'larry' };
const user = await user0.save();
expect(user.emergencyContact.name).to.equal('larry');
}); });
it('should be able retrieve json value as object', function() { it('should be able retrieve json value as object', async function() {
const emergencyContact = { name: 'kate', phone: 1337 }; const emergencyContact = { name: 'kate', phone: 1337 };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact }) const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
.then(user => { expect(user0.emergency_contact).to.eql(emergencyContact);
expect(user.emergency_contact).to.eql(emergencyContact); const user = await this.User.findOne({ where: { username: 'swen' }, attributes: ['emergency_contact'] });
return this.User.findOne({ where: { username: 'swen' }, attributes: ['emergency_contact'] });
})
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact); expect(user.emergency_contact).to.eql(emergencyContact);
}); });
});
it('should be able to retrieve element of array by index', function() { it('should be able to retrieve element of array by index', async function() {
const emergencyContact = { name: 'kate', phones: [1337, 42] }; const emergencyContact = { name: 'kate', phones: [1337, 42] };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact }) const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
.then(user => { expect(user0.emergency_contact).to.eql(emergencyContact);
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({ const user = await this.User.findOne({
where: { username: 'swen' }, where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.phones[1]'), 'firstEmergencyNumber']] attributes: [[Sequelize.json('emergency_contact.phones[1]'), 'firstEmergencyNumber']]
}); });
})
.then(user => {
expect(parseInt(user.getDataValue('firstEmergencyNumber'), 10)).to.equal(42); expect(parseInt(user.getDataValue('firstEmergencyNumber'), 10)).to.equal(42);
}); });
});
it('should be able to retrieve root level value of an object by key', function() { it('should be able to retrieve root level value of an object by key', async function() {
const emergencyContact = { kate: 1337 }; const emergencyContact = { kate: 1337 };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact }) const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
.then(user => { expect(user0.emergency_contact).to.eql(emergencyContact);
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({ const user = await this.User.findOne({
where: { username: 'swen' }, where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate'), 'katesNumber']] attributes: [[Sequelize.json('emergency_contact.kate'), 'katesNumber']]
}); });
})
.then(user => {
expect(parseInt(user.getDataValue('katesNumber'), 10)).to.equal(1337); expect(parseInt(user.getDataValue('katesNumber'), 10)).to.equal(1337);
}); });
});
it('should be able to retrieve nested value of an object by path', function() { it('should be able to retrieve nested value of an object by path', async function() {
const emergencyContact = { kate: { email: 'kate@kate.com', phones: [1337, 42] } }; const emergencyContact = { kate: { email: 'kate@kate.com', phones: [1337, 42] } };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact }) const user1 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
.then(user => { expect(user1.emergency_contact).to.eql(emergencyContact);
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({ const user0 = await this.User.findOne({
where: { username: 'swen' }, where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.email'), 'katesEmail']] attributes: [[Sequelize.json('emergency_contact.kate.email'), 'katesEmail']]
}); });
}).then(user => {
expect(user.getDataValue('katesEmail')).to.equal('kate@kate.com'); expect(user0.getDataValue('katesEmail')).to.equal('kate@kate.com');
}).then(() => {
return this.User.findOne({ const user = await this.User.findOne({
where: { username: 'swen' }, where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.phones[1]'), 'katesFirstPhone']] attributes: [[Sequelize.json('emergency_contact.kate.phones[1]'), 'katesFirstPhone']]
}); });
}).then(user => {
expect(parseInt(user.getDataValue('katesFirstPhone'), 10)).to.equal(42); expect(parseInt(user.getDataValue('katesFirstPhone'), 10)).to.equal(42);
}); });
});
it('should be able to retrieve a row based on the values of the json document', function() { it('should be able to retrieve a row based on the values of the json document', async function() {
return Promise.all([ await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }), this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } }) this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => { ]);
return this.User.findOne({
const user = await this.User.findOne({
where: Sequelize.json('emergency_contact.name', 'kate'), where: Sequelize.json('emergency_contact.name', 'kate'),
attributes: ['username', 'emergency_contact'] attributes: ['username', 'emergency_contact']
}); });
}).then(user => {
expect(user.emergency_contact.name).to.equal('kate'); expect(user.emergency_contact.name).to.equal('kate');
}); });
});
it('should be able to query using the nested query language', function() { it('should be able to query using the nested query language', async function() {
return Promise.all([ await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }), this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } }) this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => { ]);
return this.User.findOne({
const user = await this.User.findOne({
where: Sequelize.json({ emergency_contact: { name: 'kate' } }) where: Sequelize.json({ emergency_contact: { name: 'kate' } })
}); });
}).then(user => {
expect(user.emergency_contact.name).to.equal('kate'); expect(user.emergency_contact.name).to.equal('kate');
}); });
});
it('should be able to query using dot notation', function() { it('should be able to query using dot notation', async function() {
return Promise.all([ await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }), this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } }) this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => { ]);
return this.User.findOne({ where: Sequelize.json('emergency_contact.name', 'joe') });
}).then(user => { const user = await this.User.findOne({ where: Sequelize.json('emergency_contact.name', 'joe') });
expect(user.emergency_contact.name).to.equal('joe'); expect(user.emergency_contact.name).to.equal('joe');
}); });
});
it('should be able to query using dot notation with uppercase name', function() { it('should be able to query using dot notation with uppercase name', async function() {
return Promise.all([ await Promise.all([
this.User.create({ username: 'swen', emergencyContact: { name: 'kate' } }), this.User.create({ username: 'swen', emergencyContact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergencyContact: { name: 'joe' } }) this.User.create({ username: 'anna', emergencyContact: { name: 'joe' } })
]).then(() => { ]);
return this.User.findOne({
const user = await this.User.findOne({
attributes: [[Sequelize.json('emergencyContact.name'), 'contactName']], attributes: [[Sequelize.json('emergencyContact.name'), 'contactName']],
where: Sequelize.json('emergencyContact.name', 'joe') where: Sequelize.json('emergencyContact.name', 'joe')
}); });
}).then(user => {
expect(user.get('contactName')).to.equal('joe'); expect(user.get('contactName')).to.equal('joe');
}); });
});
it('should be able to query array using property accessor', function() { it('should be able to query array using property accessor', async function() {
return Promise.all([ await Promise.all([
this.User.create({ username: 'swen', emergency_contact: ['kate', 'joe'] }), this.User.create({ username: 'swen', emergency_contact: ['kate', 'joe'] }),
this.User.create({ username: 'anna', emergency_contact: [{ name: 'joe' }] }) this.User.create({ username: 'anna', emergency_contact: [{ name: 'joe' }] })
]).then(() => { ]);
return this.User.findOne({ where: Sequelize.json('emergency_contact.0', 'kate') });
}).then(user => { const user0 = await this.User.findOne({ where: Sequelize.json('emergency_contact.0', 'kate') });
expect(user.username).to.equal('swen'); expect(user0.username).to.equal('swen');
}).then(() => { const user = await this.User.findOne({ where: Sequelize.json('emergency_contact[0].name', 'joe') });
return this.User.findOne({ where: Sequelize.json('emergency_contact[0].name', 'joe') });
}).then(user => {
expect(user.username).to.equal('anna'); expect(user.username).to.equal('anna');
}); });
});
it('should be able to store values that require JSON escaping', function() { it('should be able to store values that require JSON escaping', async function() {
const text = 'Multi-line \'$string\' needing "escaping" for $$ and $1 type values'; const text = 'Multi-line \'$string\' needing "escaping" for $$ and $1 type values';
return this.User.create({ const user0 = await this.User.create({
username: 'swen', username: 'swen',
emergency_contact: { value: text } emergency_contact: { value: text }
}).then(user => {
expect(user.isNewRecord).to.equal(false);
}).then(() => {
return this.User.findOne({ where: { username: 'swen' } });
}).then(() => {
return this.User.findOne({ where: Sequelize.json('emergency_contact.value', text) });
}).then(user => {
expect(user.username).to.equal('swen');
}); });
expect(user0.isNewRecord).to.equal(false);
await this.User.findOne({ where: { username: 'swen' } });
const user = await this.User.findOne({ where: Sequelize.json('emergency_contact.value', text) });
expect(user.username).to.equal('swen');
}); });
it('should be able to findOrCreate with values that require JSON escaping', function() { it('should be able to findOrCreate with values that require JSON escaping', async function() {
const text = 'Multi-line \'$string\' needing "escaping" for $$ and $1 type values'; const text = 'Multi-line \'$string\' needing "escaping" for $$ and $1 type values';
return this.User.findOrCreate({ const user0 = await this.User.findOrCreate({
where: { username: 'swen' }, where: { username: 'swen' },
defaults: { emergency_contact: { value: text } } defaults: { emergency_contact: { value: text } }
}).then(user => {
expect(!user.isNewRecord).to.equal(true);
}).then(() => {
return this.User.findOne({ where: { username: 'swen' } });
}).then(() => {
return this.User.findOne({ where: Sequelize.json('emergency_contact.value', text) });
}).then(user => {
expect(user.username).to.equal('swen');
}); });
expect(!user0.isNewRecord).to.equal(true);
await this.User.findOne({ where: { username: 'swen' } });
const user = await this.User.findOne({ where: Sequelize.json('emergency_contact.value', text) });
expect(user.username).to.equal('swen');
}); });
// JSONB Supports this, but not JSON in postgres/mysql // JSONB Supports this, but not JSON in postgres/mysql
if (current.dialect.name === 'sqlite') { if (current.dialect.name === 'sqlite') {
it('should be able to find with just string', function() { it('should be able to find with just string', async function() {
return this.User.create({ await this.User.create({
username: 'swen123', username: 'swen123',
emergency_contact: 'Unknown' emergency_contact: 'Unknown'
}).then(() => { });
return this.User.findOne({ where: {
const user = await this.User.findOne({ where: {
emergency_contact: 'Unknown' emergency_contact: 'Unknown'
} }); } });
}).then(user => {
expect(user.username).to.equal('swen123'); expect(user.username).to.equal('swen123');
}); });
});
} }
it('should be able retrieve json value with nested include', function() { it('should be able retrieve json value with nested include', async function() {
return this.User.create({ const user = await this.User.create({
emergency_contact: { emergency_contact: {
name: 'kate' name: 'kate'
} }
}).then(user => { });
return this.Order.create({ UserId: user.id });
}).then(() => { await this.Order.create({ UserId: user.id });
return this.Order.findAll({
const orders = await this.Order.findAll({
attributes: ['id'], attributes: ['id'],
include: [{ include: [{
model: this.User, model: this.User,
...@@ -279,16 +258,15 @@ describe('model', () => { ...@@ -279,16 +258,15 @@ describe('model', () => {
] ]
}] }]
}); });
}).then(orders => {
expect(orders[0].User.getDataValue('katesName')).to.equal('kate'); expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
}); });
}); });
});
} }
if (current.dialect.supports.JSONB) { if (current.dialect.supports.JSONB) {
describe('jsonb', () => { describe('jsonb', () => {
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
emergency_contact: DataTypes.JSONB emergency_contact: DataTypes.JSONB
...@@ -296,18 +274,19 @@ describe('model', () => { ...@@ -296,18 +274,19 @@ describe('model', () => {
this.Order = this.sequelize.define('Order'); this.Order = this.sequelize.define('Order');
this.Order.belongsTo(this.User); this.Order.belongsTo(this.User);
return this.sequelize.sync({ force: true }); await this.sequelize.sync({ force: true });
}); });
it('should be able retrieve json value with nested include', function() { it('should be able retrieve json value with nested include', async function() {
return this.User.create({ const user = await this.User.create({
emergency_contact: { emergency_contact: {
name: 'kate' name: 'kate'
} }
}).then(user => { });
return this.Order.create({ UserId: user.id });
}).then(() => { await this.Order.create({ UserId: user.id });
return this.Order.findAll({
const orders = await this.Order.findAll({
attributes: ['id'], attributes: ['id'],
include: [{ include: [{
model: this.User, model: this.User,
...@@ -316,10 +295,9 @@ describe('model', () => { ...@@ -316,10 +295,9 @@ describe('model', () => {
] ]
}] }]
}); });
}).then(orders => {
expect(orders[0].User.getDataValue('katesName')).to.equal('kate'); expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
}); });
}); });
});
} }
}); });
...@@ -284,13 +284,16 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -284,13 +284,16 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}); });
await User.sync({ force: true }); await User.sync({ force: true });
try {
await Promise.all([ await Promise.all([
User.create({ username: 'tobi', email: 'tobi@tobi.me' }), User.create({ username: 'tobi', email: 'tobi@tobi.me' }),
User.create({ username: 'tobi', email: 'tobi@tobi.me' }) User.create({ username: 'tobi', email: 'tobi@tobi.me' })
]).catch(err => { ]);
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err; if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err.message).to.equal('User and email must be unique'); expect(err.message).to.equal('User and email must be unique');
}); }
}); });
// If you use migrations to create unique indexes that have explicit names and/or contain fields // If you use migrations to create unique indexes that have explicit names and/or contain fields
...@@ -319,13 +322,15 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -319,13 +322,15 @@ describe(Support.getTestDialectTeaser('Model'), () => {
email: { type: Sequelize.STRING, unique: 'user_and_email_index' } email: { type: Sequelize.STRING, unique: 'user_and_email_index' }
}); });
try {
await Promise.all([ await Promise.all([
User.create({ user_id: 1, email: 'tobi@tobi.me' }), User.create({ user_id: 1, email: 'tobi@tobi.me' }),
User.create({ user_id: 1, email: 'tobi@tobi.me' }) User.create({ user_id: 1, email: 'tobi@tobi.me' })
]).catch(err => { ]);
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err; if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err.message).to.equal('User and email must be unique'); expect(err.message).to.equal('User and email must be unique');
}); }
}); });
it('should allow the user to specify indexes in options', async function() { it('should allow the user to specify indexes in options', async function() {
...@@ -1934,9 +1939,12 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -1934,9 +1939,12 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}); });
afterEach(async function() { afterEach(async function() {
await this.sequelize.dropSchema('schema_test') try {
.finally(() => this.sequelize.dropSchema('special')) await this.sequelize.dropSchema('schema_test');
.finally(() => this.sequelize.dropSchema('prefix')); } finally {
await this.sequelize.dropSchema('special');
await this.sequelize.dropSchema('prefix');
}
}); });
it('should be able to drop with schemas', async function() { it('should be able to drop with schemas', async function() {
...@@ -2065,10 +2073,11 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2065,10 +2073,11 @@ describe(Support.getTestDialectTeaser('Model'), () => {
expect(test).to.be.true; expect(test).to.be.true;
}); });
it('should be able to create and update records under any valid schematic', function() { it('should be able to create and update records under any valid schematic', async function() {
let logged = 0; let logged = 0;
return this.UserPublic.sync({ force: true }).then(UserPublicSync => { const UserPublicSync = await this.UserPublic.sync({ force: true });
return UserPublicSync.create({ age: 3 }, {
await UserPublicSync.create({ age: 3 }, {
logging: UserPublic => { logging: UserPublic => {
logged++; logged++;
if (dialect === 'postgres') { if (dialect === 'postgres') {
...@@ -2088,8 +2097,9 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2088,8 +2097,9 @@ describe(Support.getTestDialectTeaser('Model'), () => {
expect(UserPublic).to.include('INSERT INTO `UserPublics`'); expect(UserPublic).to.include('INSERT INTO `UserPublics`');
} }
} }
}).then(() => { });
return this.UserSpecialSync.schema('special').create({ age: 3 }, {
const UserSpecial = await this.UserSpecialSync.schema('special').create({ age: 3 }, {
logging(UserSpecial) { logging(UserSpecial) {
logged++; logged++;
if (dialect === 'postgres') { if (dialect === 'postgres') {
...@@ -2104,8 +2114,9 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2104,8 +2114,9 @@ describe(Support.getTestDialectTeaser('Model'), () => {
expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`'); expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`');
} }
} }
}).then(UserSpecial => { });
return UserSpecial.update({ age: 5 }, {
await UserSpecial.update({ age: 5 }, {
logging(user) { logging(user) {
logged++; logged++;
if (dialect === 'postgres') { if (dialect === 'postgres') {
...@@ -2119,26 +2130,22 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2119,26 +2130,22 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} }
} }
}); });
});
}).then(() => {
expect(logged).to.equal(3); expect(logged).to.equal(3);
}); });
}); });
});
});
describe('references', () => { describe('references', () => {
beforeEach(function() { beforeEach(async function() {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING }); this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING });
return this.sequelize.getQueryInterface().dropTable('posts', { force: true }).then(() => { await this.sequelize.getQueryInterface().dropTable('posts', { force: true });
return this.sequelize.getQueryInterface().dropTable('authors', { force: true }); await this.sequelize.getQueryInterface().dropTable('authors', { force: true });
}).then(() => {
return this.Author.sync(); await this.Author.sync();
});
}); });
it('uses an existing dao factory and references the author table', function() { it('uses an existing dao factory and references the author table', async function() {
const authorIdColumn = { type: Sequelize.INTEGER, references: { model: this.Author, key: 'id' } }; const authorIdColumn = { type: Sequelize.INTEGER, references: { model: this.Author, key: 'id' } };
const Post = this.sequelize.define('post', { const Post = this.sequelize.define('post', {
...@@ -2150,7 +2157,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2150,7 +2157,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
Post.belongsTo(this.Author); Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter. // The posts table gets dropped in the before filter.
return Post.sync({ logging: _.once(sql => { await Post.sync({ logging: _.once(sql => {
if (dialect === 'postgres') { if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/); expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (dialect === 'mysql' || dialect === 'mariadb') { } else if (dialect === 'mysql' || dialect === 'mariadb') {
...@@ -2165,7 +2172,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2165,7 +2172,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}) }); }) });
}); });
it('uses a table name as a string and references the author table', function() { it('uses a table name as a string and references the author table', async function() {
const authorIdColumn = { type: Sequelize.INTEGER, references: { model: 'authors', key: 'id' } }; const authorIdColumn = { type: Sequelize.INTEGER, references: { model: 'authors', key: 'id' } };
const Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn }); const Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn });
...@@ -2174,7 +2181,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2174,7 +2181,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
Post.belongsTo(this.Author); Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter. // The posts table gets dropped in the before filter.
return Post.sync({ logging: _.once(sql => { await Post.sync({ logging: _.once(sql => {
if (dialect === 'postgres') { if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/); expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (dialect === 'mysql' || dialect === 'mariadb') { } else if (dialect === 'mysql' || dialect === 'mariadb') {
...@@ -2189,7 +2196,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2189,7 +2196,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}) }); }) });
}); });
it('emits an error event as the referenced table name is invalid', function() { it('emits an error event as the referenced table name is invalid', async function() {
const authorIdColumn = { type: Sequelize.INTEGER, references: { model: '4uth0r5', key: 'id' } }; const authorIdColumn = { type: Sequelize.INTEGER, references: { model: '4uth0r5', key: 'id' } };
const Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn }); const Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn });
...@@ -2197,8 +2204,9 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2197,8 +2204,9 @@ describe(Support.getTestDialectTeaser('Model'), () => {
this.Author.hasMany(Post); this.Author.hasMany(Post);
Post.belongsTo(this.Author); Post.belongsTo(this.Author);
try {
// The posts table gets dropped in the before filter. // The posts table gets dropped in the before filter.
return Post.sync().then(() => { await Post.sync();
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ... // sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).to.equal(1); expect(1).to.equal(1);
...@@ -2206,9 +2214,7 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2206,9 +2214,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
// the parser should not end up here ... // the parser should not end up here ...
expect(2).to.equal(1); expect(2).to.equal(1);
} }
} catch (err) {
return;
}).catch(err => {
if (dialect === 'mysql') { if (dialect === 'mysql') {
// MySQL 5.7 or above doesn't support POINT EMPTY // MySQL 5.7 or above doesn't support POINT EMPTY
if (semver.gte(current.options.databaseVersion, '5.6.0')) { if (semver.gte(current.options.databaseVersion, '5.6.0')) {
...@@ -2228,10 +2234,10 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2228,10 +2234,10 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} else { } else {
throw new Error('Undefined dialect!'); throw new Error('Undefined dialect!');
} }
}); }
}); });
it('works with comments', function() { it('works with comments', async function() {
// Test for a case where the comment was being moved to the end of the table when there was also a reference on the column, see #1521 // Test for a case where the comment was being moved to the end of the table when there was also a reference on the column, see #1521
const Member = this.sequelize.define('Member', {}); const Member = this.sequelize.define('Member', {});
const idColumn = { const idColumn = {
...@@ -2245,47 +2251,45 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2245,47 +2251,45 @@ describe(Support.getTestDialectTeaser('Model'), () => {
this.sequelize.define('Profile', { id: idColumn }); this.sequelize.define('Profile', { id: idColumn });
return this.sequelize.sync({ force: true }); await this.sequelize.sync({ force: true });
}); });
}); });
describe('blob', () => { describe('blob', () => {
beforeEach(function() { beforeEach(async function() {
this.BlobUser = this.sequelize.define('blobUser', { this.BlobUser = this.sequelize.define('blobUser', {
data: Sequelize.BLOB data: Sequelize.BLOB
}); });
return this.BlobUser.sync({ force: true }); await this.BlobUser.sync({ force: true });
}); });
describe('buffers', () => { describe('buffers', () => {
it('should be able to take a buffer as parameter to a BLOB field', function() { it('should be able to take a buffer as parameter to a BLOB field', async function() {
return this.BlobUser.create({ const user = await this.BlobUser.create({
data: Buffer.from('Sequelize') data: Buffer.from('Sequelize')
}).then(user => {
expect(user).to.be.ok;
}); });
expect(user).to.be.ok;
}); });
it('should return a buffer when fetching a blob', function() { it('should return a buffer when fetching a blob', async function() {
return this.BlobUser.create({ const user = await this.BlobUser.create({
data: Buffer.from('Sequelize') data: Buffer.from('Sequelize')
}).then(user => {
return this.BlobUser.findByPk(user.id).then(user => {
expect(user.data).to.be.an.instanceOf(Buffer);
expect(user.data.toString()).to.have.string('Sequelize');
});
}); });
const user0 = await this.BlobUser.findByPk(user.id);
expect(user0.data).to.be.an.instanceOf(Buffer);
expect(user0.data.toString()).to.have.string('Sequelize');
}); });
it('should work when the database returns null', function() { it('should work when the database returns null', async function() {
return this.BlobUser.create({ const user = await this.BlobUser.create({
// create a null column // create a null column
}).then(user => {
return this.BlobUser.findByPk(user.id).then(user => {
expect(user.data).to.be.null;
});
}); });
const user0 = await this.BlobUser.findByPk(user.id);
expect(user0.data).to.be.null;
}); });
}); });
...@@ -2296,23 +2300,22 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2296,23 +2300,22 @@ describe(Support.getTestDialectTeaser('Model'), () => {
// data is passed in, in string form? Very unclear, and very different. // data is passed in, in string form? Very unclear, and very different.
describe('strings', () => { describe('strings', () => {
it('should be able to take a string as parameter to a BLOB field', function() { it('should be able to take a string as parameter to a BLOB field', async function() {
return this.BlobUser.create({ const user = await this.BlobUser.create({
data: 'Sequelize' data: 'Sequelize'
}).then(user => {
expect(user).to.be.ok;
}); });
expect(user).to.be.ok;
}); });
it('should return a buffer when fetching a BLOB, even when the BLOB was inserted as a string', function() { it('should return a buffer when fetching a BLOB, even when the BLOB was inserted as a string', async function() {
return this.BlobUser.create({ const user = await this.BlobUser.create({
data: 'Sequelize' data: 'Sequelize'
}).then(user => {
return this.BlobUser.findByPk(user.id).then(user => {
expect(user.data).to.be.an.instanceOf(Buffer);
expect(user.data.toString()).to.have.string('Sequelize');
});
}); });
const user0 = await this.BlobUser.findByPk(user.id);
expect(user0.data).to.be.an.instanceOf(Buffer);
expect(user0.data.toString()).to.have.string('Sequelize');
}); });
}); });
} }
...@@ -2321,96 +2324,92 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2321,96 +2324,92 @@ describe(Support.getTestDialectTeaser('Model'), () => {
describe('paranoid is true and where is an array', () => { describe('paranoid is true and where is an array', () => {
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { username: DataTypes.STRING }, { paranoid: true }); this.User = this.sequelize.define('User', { username: DataTypes.STRING }, { paranoid: true });
this.Project = this.sequelize.define('Project', { title: DataTypes.STRING }, { paranoid: true }); this.Project = this.sequelize.define('Project', { title: DataTypes.STRING }, { paranoid: true });
this.Project.belongsToMany(this.User, { through: 'project_user' }); this.Project.belongsToMany(this.User, { through: 'project_user' });
this.User.belongsToMany(this.Project, { through: 'project_user' }); this.User.belongsToMany(this.Project, { through: 'project_user' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return this.User.bulkCreate([{
await this.User.bulkCreate([{
username: 'leia' username: 'leia'
}, { }, {
username: 'luke' username: 'luke'
}, { }, {
username: 'vader' username: 'vader'
}]).then(() => { }]);
return this.Project.bulkCreate([{
await this.Project.bulkCreate([{
title: 'republic' title: 'republic'
}, { }, {
title: 'empire' title: 'empire'
}]).then(() => { }]);
return this.User.findAll().then(users => {
return this.Project.findAll().then(projects => { const users = await this.User.findAll();
const projects = await this.Project.findAll();
const leia = users[0], const leia = users[0],
luke = users[1], luke = users[1],
vader = users[2], vader = users[2],
republic = projects[0], republic = projects[0],
empire = projects[1]; empire = projects[1];
return leia.setProjects([republic]).then(() => { await leia.setProjects([republic]);
return luke.setProjects([republic]).then(() => { await luke.setProjects([republic]);
return vader.setProjects([empire]).then(() => { await vader.setProjects([empire]);
return leia.destroy();
}); await leia.destroy();
});
});
});
});
});
});
});
}); });
it('should not fail when array contains Sequelize.or / and', function() { it('should not fail when array contains Sequelize.or / and', async function() {
return this.User.findAll({ const res = await this.User.findAll({
where: [ where: [
this.sequelize.or({ username: 'vader' }, { username: 'luke' }), this.sequelize.or({ username: 'vader' }, { username: 'luke' }),
this.sequelize.and({ id: [1, 2, 3] }) this.sequelize.and({ id: [1, 2, 3] })
] ]
})
.then(res => {
expect(res).to.have.length(2);
}); });
expect(res).to.have.length(2);
}); });
it('should fail when array contains strings', function() { it('should fail when array contains strings', async function() {
return expect(this.User.findAll({ await expect(this.User.findAll({
where: ['this is a mistake', ['dont do it!']] where: ['this is a mistake', ['dont do it!']]
})).to.eventually.be.rejectedWith(Error, 'Support for literal replacements in the `where` object has been removed.'); })).to.eventually.be.rejectedWith(Error, 'Support for literal replacements in the `where` object has been removed.');
}); });
it('should not fail with an include', function() { it('should not fail with an include', async function() {
return this.User.findAll({ const users = await this.User.findAll({
where: this.sequelize.literal(`${this.sequelize.queryInterface.queryGenerator.quoteIdentifiers('Projects.title')} = ${this.sequelize.queryInterface.queryGenerator.escape('republic')}`), where: this.sequelize.literal(`${this.sequelize.queryInterface.queryGenerator.quoteIdentifiers('Projects.title')} = ${this.sequelize.queryInterface.queryGenerator.escape('republic')}`),
include: [ include: [
{ model: this.Project } { model: this.Project }
] ]
}).then(users => { });
expect(users.length).to.be.equal(1); expect(users.length).to.be.equal(1);
expect(users[0].username).to.be.equal('luke'); expect(users[0].username).to.be.equal('luke');
}); });
});
it('should not overwrite a specified deletedAt by setting paranoid: false', function() { it('should not overwrite a specified deletedAt by setting paranoid: false', async function() {
let tableName = ''; let tableName = '';
if (this.User.name) { if (this.User.name) {
tableName = `${this.sequelize.queryInterface.queryGenerator.quoteIdentifier(this.User.name)}.`; tableName = `${this.sequelize.queryInterface.queryGenerator.quoteIdentifier(this.User.name)}.`;
} }
return this.User.findAll({
const users = await this.User.findAll({
paranoid: false, paranoid: false,
where: this.sequelize.literal(`${tableName + this.sequelize.queryInterface.queryGenerator.quoteIdentifier('deletedAt')} IS NOT NULL `), where: this.sequelize.literal(`${tableName + this.sequelize.queryInterface.queryGenerator.quoteIdentifier('deletedAt')} IS NOT NULL `),
include: [ include: [
{ model: this.Project } { model: this.Project }
] ]
}).then(users => { });
expect(users.length).to.be.equal(1); expect(users.length).to.be.equal(1);
expect(users[0].username).to.be.equal('leia'); expect(users[0].username).to.be.equal('leia');
}); });
});
it('should not overwrite a specified deletedAt (complex query) by setting paranoid: false', function() { it('should not overwrite a specified deletedAt (complex query) by setting paranoid: false', async function() {
return this.User.findAll({ const res = await this.User.findAll({
paranoid: false, paranoid: false,
where: [ where: [
this.sequelize.or({ username: 'leia' }, { username: 'luke' }), this.sequelize.or({ username: 'leia' }, { username: 'luke' }),
...@@ -2419,98 +2418,94 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2419,98 +2418,94 @@ describe(Support.getTestDialectTeaser('Model'), () => {
this.sequelize.or({ deletedAt: null }, { deletedAt: { [Op.gt]: new Date(0) } }) this.sequelize.or({ deletedAt: null }, { deletedAt: { [Op.gt]: new Date(0) } })
) )
] ]
})
.then(res => {
expect(res).to.have.length(2);
}); });
expect(res).to.have.length(2);
}); });
}); });
if (dialect !== 'sqlite' && current.dialect.supports.transactions) { if (dialect !== 'sqlite' && current.dialect.supports.transactions) {
it('supports multiple async transactions', function() { it('supports multiple async transactions', async function() {
this.timeout(90000); this.timeout(90000);
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { username: Sequelize.STRING }); const User = sequelize.define('User', { username: Sequelize.STRING });
const testAsync = function() { const testAsync = async function() {
return sequelize.transaction().then(t => { const t0 = await sequelize.transaction();
return User.create({
await User.create({
username: 'foo' username: 'foo'
}, { }, {
transaction: t transaction: t0
}).then(() => { });
return User.findAll({
const users0 = await User.findAll({
where: { where: {
username: 'foo' username: 'foo'
} }
}).then(users => {
expect(users).to.have.length(0);
}); });
}).then(() => {
return User.findAll({ expect(users0).to.have.length(0);
const users = await User.findAll({
where: { where: {
username: 'foo' username: 'foo'
}, },
transaction: t transaction: t0
}).then(users => {
expect(users).to.have.length(1);
}); });
}).then(() => {
return t; expect(users).to.have.length(1);
}); const t = t0;
}).then(t => {
return t.rollback(); return t.rollback();
});
}; };
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
const tasks = []; const tasks = [];
for (let i = 0; i < 1000; i++) { for (let i = 0; i < 1000; i++) {
tasks.push(testAsync); tasks.push(testAsync);
} }
return pMap(tasks, entry => {
await pMap(tasks, entry => {
return entry(); return entry();
}, { }, {
// Needs to be one less than ??? else the non transaction query won't ever get a connection // Needs to be one less than ??? else the non transaction query won't ever get a connection
concurrency: (sequelize.config.pool && sequelize.config.pool.max || 5) - 1 concurrency: (sequelize.config.pool && sequelize.config.pool.max || 5) - 1
}); });
}); });
});
});
} }
describe('Unique', () => { describe('Unique', () => {
it('should set unique when unique is true', function() { it('should set unique when unique is true', async function() {
const uniqueTrue = this.sequelize.define('uniqueTrue', { const uniqueTrue = this.sequelize.define('uniqueTrue', {
str: { type: Sequelize.STRING, unique: true } str: { type: Sequelize.STRING, unique: true }
}); });
return uniqueTrue.sync({ force: true, logging: _.after(2, _.once(s => { await uniqueTrue.sync({ force: true, logging: _.after(2, _.once(s => {
expect(s).to.match(/UNIQUE/); expect(s).to.match(/UNIQUE/);
})) }); })) });
}); });
it('should not set unique when unique is false', function() { it('should not set unique when unique is false', async function() {
const uniqueFalse = this.sequelize.define('uniqueFalse', { const uniqueFalse = this.sequelize.define('uniqueFalse', {
str: { type: Sequelize.STRING, unique: false } str: { type: Sequelize.STRING, unique: false }
}); });
return uniqueFalse.sync({ force: true, logging: _.after(2, _.once(s => { await uniqueFalse.sync({ force: true, logging: _.after(2, _.once(s => {
expect(s).not.to.match(/UNIQUE/); expect(s).not.to.match(/UNIQUE/);
})) }); })) });
}); });
it('should not set unique when unique is unset', function() { it('should not set unique when unique is unset', async function() {
const uniqueUnset = this.sequelize.define('uniqueUnset', { const uniqueUnset = this.sequelize.define('uniqueUnset', {
str: { type: Sequelize.STRING } str: { type: Sequelize.STRING }
}); });
return uniqueUnset.sync({ force: true, logging: _.after(2, _.once(s => { await uniqueUnset.sync({ force: true, logging: _.after(2, _.once(s => {
expect(s).not.to.match(/UNIQUE/); expect(s).not.to.match(/UNIQUE/);
})) }); })) });
}); });
}); });
it('should be possible to use a key named UUID as foreign key', function() { it('should be possible to use a key named UUID as foreign key', async function() {
this.sequelize.define('project', { this.sequelize.define('project', {
UserId: { UserId: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -2534,11 +2529,11 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2534,11 +2529,11 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} }
}); });
return this.sequelize.sync({ force: true }); await this.sequelize.sync({ force: true });
}); });
describe('bulkCreate', () => { describe('bulkCreate', () => {
it('errors - should return array of errors if validate and individualHooks are true', function() { it('errors - should return array of errors if validate and individualHooks are true', async function() {
const data = [{ username: null }, const data = [{ username: null },
{ username: null }, { username: null },
{ username: null }]; { username: null }];
...@@ -2554,15 +2549,14 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2554,15 +2549,14 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} }
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
expect(user.bulkCreate(data, { expect(user.bulkCreate(data, {
validate: true, validate: true,
individualHooks: true individualHooks: true
})).to.be.rejectedWith(errors.AggregateError); })).to.be.rejectedWith(errors.AggregateError);
}); });
});
it('should not use setter when renaming fields in dataValues', function() { it('should not use setter when renaming fields in dataValues', async function() {
const user = this.sequelize.define('User', { const user = this.sequelize.define('User', {
username: { username: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -2582,13 +2576,10 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -2582,13 +2576,10 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}); });
const data = [{ username: 'jon' }]; const data = [{ username: 'jon' }];
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return user.bulkCreate(data).then(() => { await user.bulkCreate(data);
return user.findAll().then(users1 => { const users1 = await user.findAll();
expect(users1[0].username).to.equal('jon'); expect(users1[0].username).to.equal('jon');
}); });
}); });
});
});
});
}); });
...@@ -13,7 +13,7 @@ describe(Support.getTestDialectTeaser('Replication'), () => { ...@@ -13,7 +13,7 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
let sandbox; let sandbox;
let readSpy, writeSpy; let readSpy, writeSpy;
beforeEach(function() { beforeEach(async function() {
sandbox = sinon.createSandbox(); sandbox = sinon.createSandbox();
this.sequelize = Support.getSequelizeInstance(null, null, null, { this.sequelize = Support.getSequelizeInstance(null, null, null, {
...@@ -33,12 +33,10 @@ describe(Support.getTestDialectTeaser('Replication'), () => { ...@@ -33,12 +33,10 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
} }
}); });
return this.User.sync({ force: true }) await this.User.sync({ force: true });
.then(() => {
readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire'); readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire');
writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire'); writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire');
}); });
});
afterEach(() => { afterEach(() => {
sandbox.restore(); sandbox.restore();
...@@ -54,25 +52,25 @@ describe(Support.getTestDialectTeaser('Replication'), () => { ...@@ -54,25 +52,25 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
chai.expect(readSpy.notCalled).eql(true); chai.expect(readSpy.notCalled).eql(true);
} }
it('should be able to make a write', function() { it('should be able to make a write', async function() {
return this.User.create({ await expectWriteCalls(await this.User.create({
firstName: Math.random().toString() firstName: Math.random().toString()
}).then(expectWriteCalls); }));
}); });
it('should be able to make a read', function() { it('should be able to make a read', async function() {
return this.User.findAll().then(expectReadCalls); await expectReadCalls(await this.User.findAll());
}); });
it('should run read-only transactions on the replica', function() { it('should run read-only transactions on the replica', async function() {
return this.sequelize.transaction({ readOnly: true }, transaction => { await expectReadCalls(await this.sequelize.transaction({ readOnly: true }, transaction => {
return this.User.findAll({ transaction }); return this.User.findAll({ transaction });
}).then(expectReadCalls); }));
}); });
it('should run non-read-only transactions on the primary', function() { it('should run non-read-only transactions on the primary', async function() {
return this.sequelize.transaction(transaction => { await expectWriteCalls(await this.sequelize.transaction(transaction => {
return this.User.findAll({ transaction }); return this.User.findAll({ transaction });
}).then(expectWriteCalls); }));
}); });
}); });
...@@ -6,43 +6,37 @@ const chai = require('chai'), ...@@ -6,43 +6,37 @@ const chai = require('chai'),
DataTypes = require('../../lib/data-types'); DataTypes = require('../../lib/data-types');
describe(Support.getTestDialectTeaser('Schema'), () => { describe(Support.getTestDialectTeaser('Schema'), () => {
beforeEach(function() { beforeEach(async function() {
return this.sequelize.createSchema('testschema'); await this.sequelize.createSchema('testschema');
}); });
afterEach(function() { afterEach(async function() {
return this.sequelize.dropSchema('testschema'); await this.sequelize.dropSchema('testschema');
}); });
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
aNumber: { type: DataTypes.INTEGER } aNumber: { type: DataTypes.INTEGER }
}, { }, {
schema: 'testschema' schema: 'testschema'
}); });
return this.User.sync({ force: true }); await this.User.sync({ force: true });
}); });
it('supports increment', function() { it('supports increment', async function() {
return this.User.create({ aNumber: 1 }).then(user => { const user0 = await this.User.create({ aNumber: 1 });
return user.increment('aNumber', { by: 3 }); const result = await user0.increment('aNumber', { by: 3 });
}).then(result => { const user = await result.reload();
return result.reload();
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(4); expect(user.aNumber).to.be.equal(4);
}); });
});
it('supports decrement', function() { it('supports decrement', async function() {
return this.User.create({ aNumber: 10 }).then(user => { const user0 = await this.User.create({ aNumber: 10 });
return user.decrement('aNumber', { by: 3 }); const result = await user0.decrement('aNumber', { by: 3 });
}).then(result => { const user = await result.reload();
return result.reload();
}).then(user => {
expect(user).to.be.ok; expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(7); expect(user.aNumber).to.be.equal(7);
}); });
});
}); });
...@@ -77,8 +77,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -77,8 +77,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (dialect !== 'sqlite') { if (dialect !== 'sqlite') {
describe('authenticate', () => { describe('authenticate', () => {
describe('with valid credentials', () => { describe('with valid credentials', () => {
it('triggers the success event', function() { it('triggers the success event', async function() {
return this.sequelize.authenticate(); await this.sequelize.authenticate();
}); });
}); });
...@@ -88,32 +88,35 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -88,32 +88,35 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.sequelizeWithInvalidConnection = new Sequelize('wat', 'trololo', 'wow', options); this.sequelizeWithInvalidConnection = new Sequelize('wat', 'trololo', 'wow', options);
}); });
it('triggers the error event', function() { it('triggers the error event', async function() {
return this try {
await this
.sequelizeWithInvalidConnection .sequelizeWithInvalidConnection
.authenticate() .authenticate();
.catch(err => { } catch (err) {
expect(err).to.not.be.null; expect(err).to.not.be.null;
}); }
}); });
it('triggers an actual RangeError or ConnectionError', function() { it('triggers an actual RangeError or ConnectionError', async function() {
return this try {
await this
.sequelizeWithInvalidConnection .sequelizeWithInvalidConnection
.authenticate() .authenticate();
.catch(err => { } catch (err) {
expect( expect(
err instanceof RangeError || err instanceof RangeError ||
err instanceof Sequelize.ConnectionError err instanceof Sequelize.ConnectionError
).to.be.ok; ).to.be.ok;
}); }
}); });
it('triggers the actual adapter error', function() { it('triggers the actual adapter error', async function() {
return this try {
await this
.sequelizeWithInvalidConnection .sequelizeWithInvalidConnection
.authenticate() .authenticate();
.catch(err => { } catch (err) {
console.log(err); console.log(err);
expect( expect(
err.message.includes('connect ECONNREFUSED') || err.message.includes('connect ECONNREFUSED') ||
...@@ -122,7 +125,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -122,7 +125,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
err.message.includes('Login failed for user') || err.message.includes('Login failed for user') ||
err.message.includes('must be > 0 and < 65536') err.message.includes('must be > 0 and < 65536')
).to.be.ok; ).to.be.ok;
}); }
}); });
}); });
...@@ -131,26 +134,29 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -131,26 +134,29 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.sequelizeWithInvalidCredentials = new Sequelize('localhost', 'wtf', 'lol', this.sequelize.options); this.sequelizeWithInvalidCredentials = new Sequelize('localhost', 'wtf', 'lol', this.sequelize.options);
}); });
it('triggers the error event', function() { it('triggers the error event', async function() {
return this try {
await this
.sequelizeWithInvalidCredentials .sequelizeWithInvalidCredentials
.authenticate() .authenticate();
.catch(err => { } catch (err) {
expect(err).to.not.be.null; expect(err).to.not.be.null;
}); }
}); });
it('triggers an actual sequlize error', function() { it('triggers an actual sequlize error', async function() {
return this try {
await this
.sequelizeWithInvalidCredentials .sequelizeWithInvalidCredentials
.authenticate() .authenticate();
.catch(err => { } catch (err) {
expect(err).to.be.instanceof(Sequelize.Error); expect(err).to.be.instanceof(Sequelize.Error);
}); }
}); });
it('triggers the error event when using replication', () => { it('triggers the error event when using replication', async () => {
return new Sequelize('sequelize', null, null, { try {
await new Sequelize('sequelize', null, null, {
dialect, dialect,
replication: { replication: {
read: { read: {
...@@ -159,10 +165,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -159,10 +165,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
password: 'lol' password: 'lol'
} }
} }
}).authenticate() }).authenticate();
.catch(err => { } catch (err) {
expect(err).to.not.be.null; expect(err).to.not.be.null;
}); }
}); });
}); });
}); });
...@@ -221,7 +227,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -221,7 +227,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
console.log.restore && console.log.restore(); console.log.restore && console.log.restore();
}); });
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: { username: {
type: DataTypes.STRING, type: DataTypes.STRING,
...@@ -237,37 +243,37 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -237,37 +243,37 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
qq('createdAt') }, ${qq('updatedAt') qq('createdAt') }, ${qq('updatedAt')
}) VALUES ('john', 'john@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10')`; }) VALUES ('john', 'john@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10')`;
return this.User.sync({ force: true }); await this.User.sync({ force: true });
}); });
it('executes a query the internal way', function() { it('executes a query the internal way', async function() {
return this.sequelize.query(this.insertQuery, { raw: true }); await this.sequelize.query(this.insertQuery, { raw: true });
}); });
it('executes a query if only the sql is passed', function() { it('executes a query if only the sql is passed', async function() {
return this.sequelize.query(this.insertQuery); await this.sequelize.query(this.insertQuery);
}); });
it('executes a query if a placeholder value is an array', function() { it('executes a query if a placeholder value is an array', async function() {
return this.sequelize.query(`INSERT INTO ${qq(this.User.tableName)} (username, email_address, ` + await this.sequelize.query(`INSERT INTO ${qq(this.User.tableName)} (username, email_address, ` +
`${qq('createdAt')}, ${qq('updatedAt')}) VALUES ?;`, { `${qq('createdAt')}, ${qq('updatedAt')}) VALUES ?;`, {
replacements: [[ replacements: [[
['john', 'john@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10'], ['john', 'john@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10'],
['michael', 'michael@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10'] ['michael', 'michael@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10']
]] ]]
}) });
.then(() => this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
const rows = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: this.sequelize.QueryTypes.SELECT type: this.sequelize.QueryTypes.SELECT
})) });
.then(rows => {
expect(rows).to.be.lengthOf(2); expect(rows).to.be.lengthOf(2);
expect(rows[0].username).to.be.equal('john'); expect(rows[0].username).to.be.equal('john');
expect(rows[1].username).to.be.equal('michael'); expect(rows[1].username).to.be.equal('michael');
}); });
});
describe('retry', () => { describe('retry', () => {
it('properly bind parameters on extra retries', function() { it('properly bind parameters on extra retries', async function() {
const payload = { const payload = {
username: 'test', username: 'test',
createdAt: '2010-10-10 00:00:00', createdAt: '2010-10-10 00:00:00',
...@@ -276,7 +282,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -276,7 +282,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
const spy = sinon.spy(); const spy = sinon.spy();
return expect(this.User.create(payload).then(() => this.sequelize.query(` await this.User.create(payload);
await expect(this.sequelize.query(`
INSERT INTO ${qq(this.User.tableName)} (username,${qq('createdAt')},${qq('updatedAt')}) VALUES ($username,$createdAt,$updatedAt); INSERT INTO ${qq(this.User.tableName)} (username,${qq('createdAt')},${qq('updatedAt')}) VALUES ($username,$createdAt,$updatedAt);
`, { `, {
bind: payload, bind: payload,
...@@ -287,41 +295,40 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -287,41 +295,40 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
/Validation/ /Validation/
] ]
} }
}))).to.be.rejectedWith(Sequelize.UniqueConstraintError).then(() => { })).to.be.rejectedWith(Sequelize.UniqueConstraintError);
expect(spy.callCount).to.eql(3); expect(spy.callCount).to.eql(3);
}); });
}); });
});
describe('logging', () => { describe('logging', () => {
it('executes a query with global benchmarking option and custom logger', () => { it('executes a query with global benchmarking option and custom logger', async () => {
const logger = sinon.spy(); const logger = sinon.spy();
const sequelize = Support.createSequelizeInstance({ const sequelize = Support.createSequelizeInstance({
logging: logger, logging: logger,
benchmark: true benchmark: true
}); });
return sequelize.query('select 1;').then(() => { await sequelize.query('select 1;');
expect(logger.calledOnce).to.be.true; expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \((\d*|default)\): select 1/); expect(logger.args[0][0]).to.be.match(/Executed \((\d*|default)\): select 1/);
expect(typeof logger.args[0][1] === 'number').to.be.true; expect(typeof logger.args[0][1] === 'number').to.be.true;
}); });
});
it('executes a query with benchmarking option and custom logger', function() { it('executes a query with benchmarking option and custom logger', async function() {
const logger = sinon.spy(); const logger = sinon.spy();
return this.sequelize.query('select 1;', { await this.sequelize.query('select 1;', {
logging: logger, logging: logger,
benchmark: true benchmark: true
}).then(() => { });
expect(logger.calledOnce).to.be.true; expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \(\d*|default\): select 1;/); expect(logger.args[0][0]).to.be.match(/Executed \(\d*|default\): select 1;/);
expect(typeof logger.args[0][1] === 'number').to.be.true; expect(typeof logger.args[0][1] === 'number').to.be.true;
}); });
});
describe('log sql when set logQueryParameters', () => { describe('log sql when set logQueryParameters', () => {
beforeEach(function() { beforeEach(async function() {
this.sequelize = Support.createSequelizeInstance({ this.sequelize = Support.createSequelizeInstance({
benchmark: true, benchmark: true,
logQueryParameters: true logQueryParameters: true
...@@ -342,175 +349,163 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -342,175 +349,163 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
timestamps: false timestamps: false
}); });
return this.User.sync({ force: true }); await this.User.sync({ force: true });
}); });
it('add parameters in log sql', function() { it('add parameters in log sql', async function() {
let createSql, updateSql; let createSql, updateSql;
return this.User.create({
const user = await this.User.create({
username: 'john', username: 'john',
emailAddress: 'john@gmail.com' emailAddress: 'john@gmail.com'
}, { }, {
logging: s =>{ logging: s =>{
createSql = s; createSql = s;
} }
}).then(user=>{ });
user.username = 'li'; user.username = 'li';
return user.save({
await user.save({
logging: s =>{ logging: s =>{
updateSql = s; updateSql = s;
} }
}); });
}).then(()=>{
expect(createSql).to.match(/; ("john", "john@gmail.com"|{"(\$1|0)":"john","(\$2|1)":"john@gmail.com"})/); expect(createSql).to.match(/; ("john", "john@gmail.com"|{"(\$1|0)":"john","(\$2|1)":"john@gmail.com"})/);
expect(updateSql).to.match(/; ("li", 1|{"(\$1|0)":"li","(\$2|1)":1})/); expect(updateSql).to.match(/; ("li", 1|{"(\$1|0)":"li","(\$2|1)":1})/);
}); });
});
it('add parameters in log sql when use bind value', function() { it('add parameters in log sql when use bind value', async function() {
let logSql; let logSql;
const typeCast = dialect === 'postgres' ? '::text' : ''; const typeCast = dialect === 'postgres' ? '::text' : '';
return this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { bind: ['foo', 'bar'], logging: s=>logSql = s }) await this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { bind: ['foo', 'bar'], logging: s=>logSql = s });
.then(()=>{
expect(logSql).to.match(/; ("foo", "bar"|{"(\$1|0)":"foo","(\$2|1)":"bar"})/); expect(logSql).to.match(/; ("foo", "bar"|{"(\$1|0)":"foo","(\$2|1)":"bar"})/);
}); });
}); });
});
}); });
it('executes select queries correctly', function() { it('executes select queries correctly', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query(`select * from ${qq(this.User.tableName)}`); const [users] = await this.sequelize.query(`select * from ${qq(this.User.tableName)}`);
}).then(([users]) => {
expect(users.map(u => { return u.username; })).to.include('john'); expect(users.map(u => { return u.username; })).to.include('john');
}); });
});
it('executes select queries correctly when quoteIdentifiers is false', function() { it('executes select queries correctly when quoteIdentifiers is false', async function() {
const seq = Object.create(this.sequelize); const seq = Object.create(this.sequelize);
seq.options.quoteIdentifiers = false; seq.options.quoteIdentifiers = false;
return seq.query(this.insertQuery).then(() => { await seq.query(this.insertQuery);
return seq.query(`select * from ${qq(this.User.tableName)}`); const [users] = await seq.query(`select * from ${qq(this.User.tableName)}`);
}).then(([users]) => {
expect(users.map(u => { return u.username; })).to.include('john'); expect(users.map(u => { return u.username; })).to.include('john');
}); });
});
it('executes select query with dot notation results', function() { it('executes select query with dot notation results', async function() {
return this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`).then(() => { await this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`);
return this.sequelize.query(this.insertQuery); await this.sequelize.query(this.insertQuery);
}).then(() => { const [users] = await this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`);
return this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`);
}).then(([users]) => {
expect(users).to.deep.equal([{ 'user.username': 'john' }]); expect(users).to.deep.equal([{ 'user.username': 'john' }]);
}); });
});
it('executes select query with dot notation results and nest it', function() { it('executes select query with dot notation results and nest it', async function() {
return this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`).then(() => { await this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`);
return this.sequelize.query(this.insertQuery); await this.sequelize.query(this.insertQuery);
}).then(() => { const users = await this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`, { raw: true, nest: true });
return this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`, { raw: true, nest: true });
}).then(users => {
expect(users.map(u => { return u.user; })).to.deep.equal([{ 'username': 'john' }]); expect(users.map(u => { return u.user; })).to.deep.equal([{ 'username': 'john' }]);
}); });
});
if (dialect === 'mysql') { if (dialect === 'mysql') {
it('executes stored procedures', function() { it('executes stored procedures', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query('DROP PROCEDURE IF EXISTS foo').then(() => { await this.sequelize.query('DROP PROCEDURE IF EXISTS foo');
return this.sequelize.query(
await this.sequelize.query(
`CREATE PROCEDURE foo()\nSELECT * FROM ${this.User.tableName};` `CREATE PROCEDURE foo()\nSELECT * FROM ${this.User.tableName};`
).then(() => { );
return this.sequelize.query('CALL foo()').then(users => {
const users = await this.sequelize.query('CALL foo()');
expect(users.map(u => { return u.username; })).to.include('john'); expect(users.map(u => { return u.username; })).to.include('john');
}); });
});
});
});
});
} else { } else {
console.log('FIXME: I want to be supported in this dialect as well :-('); console.log('FIXME: I want to be supported in this dialect as well :-(');
} }
it('uses the passed model', function() { it('uses the passed model', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User model: this.User
}); });
}).then(users => {
expect(users[0]).to.be.instanceof(this.User); expect(users[0]).to.be.instanceof(this.User);
}); });
});
it('maps the field names to attributes based on the passed model', function() { it('maps the field names to attributes based on the passed model', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User, model: this.User,
mapToModel: true mapToModel: true
}); });
}).then(users => {
expect(users[0].emailAddress).to.be.equal('john@gmail.com'); expect(users[0].emailAddress).to.be.equal('john@gmail.com');
}); });
});
it('arbitrarily map the field names', function() { it('arbitrarily map the field names', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT', type: 'SELECT',
fieldMap: { username: 'userName', email_address: 'email' } fieldMap: { username: 'userName', email_address: 'email' }
}); });
}).then(users => {
expect(users[0].userName).to.be.equal('john'); expect(users[0].userName).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com'); expect(users[0].email).to.be.equal('john@gmail.com');
}); });
});
it('keeps field names that are mapped to the same name', function() { it('keeps field names that are mapped to the same name', async function() {
return this.sequelize.query(this.insertQuery).then(() => { await this.sequelize.query(this.insertQuery);
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT', type: 'SELECT',
fieldMap: { username: 'username', email_address: 'email' } fieldMap: { username: 'username', email_address: 'email' }
}); });
}).then(users => {
expect(users[0].username).to.be.equal('john'); expect(users[0].username).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com'); expect(users[0].email).to.be.equal('john@gmail.com');
}); });
});
it('reject if `values` and `options.replacements` are both passed', function() { it('reject if `values` and `options.replacements` are both passed', async function() {
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { raw: true, replacements: [1, 2] }) await this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { raw: true, replacements: [1, 2] })
.should.be.rejectedWith(Error, 'Both `sql.values` and `options.replacements` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `sql.values` and `options.replacements` cannot be set at the same time');
}); });
it('reject if `sql.bind` and `options.bind` are both passed', function() { it('reject if `sql.bind` and `options.bind` are both passed', async function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, bind: [1, 2] }) await this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, 'Both `sql.bind` and `options.bind` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `sql.bind` and `options.bind` cannot be set at the same time');
}); });
it('reject if `options.replacements` and `options.bind` are both passed', function() { it('reject if `options.replacements` and `options.bind` are both passed', async function() {
return this.sequelize.query('select $1 + ? as foo, $2 + ? as bar', { raw: true, bind: [1, 2], replacements: [1, 2] }) await this.sequelize.query('select $1 + ? as foo, $2 + ? as bar', { raw: true, bind: [1, 2], replacements: [1, 2] })
.should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time');
}); });
it('reject if `sql.bind` and `sql.values` are both passed', function() { it('reject if `sql.bind` and `sql.values` are both passed', async function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2], values: [1, 2] }, { raw: true }) await this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2], values: [1, 2] }, { raw: true })
.should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time');
}); });
it('reject if `sql.bind` and `options.replacements`` are both passed', function() { it('reject if `sql.bind` and `options.replacements`` are both passed', async function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, replacements: [1, 2] }) await this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, replacements: [1, 2] })
.should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time');
}); });
it('reject if `options.bind` and `sql.replacements` are both passed', function() { it('reject if `options.bind` and `sql.replacements` are both passed', async function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $1 _ ? as bar', values: [1, 2] }, { raw: true, bind: [1, 2] }) await this.sequelize.query({ query: 'select $1 + ? as foo, $1 _ ? as bar', values: [1, 2] }, { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time'); .should.be.rejectedWith(Error, 'Both `replacements` and `bind` cannot be set at the same time');
}); });
it('properly adds and escapes replacement value', function() { it('properly adds and escapes replacement value', async function() {
let logSql; let logSql;
const number = 1, const number = 1,
date = new Date(), date = new Date(),
...@@ -519,7 +514,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -519,7 +514,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
buffer = Buffer.from('t\'e"st'); buffer = Buffer.from('t\'e"st');
date.setMilliseconds(0); date.setMilliseconds(0);
return this.sequelize.query({
const result = await this.sequelize.query({
query: 'select ? as number, ? as date,? as string,? as boolean,? as buffer', query: 'select ? as number, ? as date,? as string,? as boolean,? as buffer',
values: [number, date, string, boolean, buffer] values: [number, date, string, boolean, buffer]
}, { }, {
...@@ -527,7 +523,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -527,7 +523,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
logging(s) { logging(s) {
logSql = s; logSql = s;
} }
}).then(result => { });
const res = result[0] || {}; const res = result[0] || {};
res.date = res.date && new Date(res.date); res.date = res.date && new Date(res.date);
res.boolean = res.boolean && true; res.boolean = res.boolean && true;
...@@ -543,9 +540,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -543,9 +540,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}); });
expect(logSql).to.not.include('?'); expect(logSql).to.not.include('?');
}); });
});
it('it allows to pass custom class instances', function() { it('it allows to pass custom class instances', async function() {
let logSql; let logSql;
class SQLStatement { class SQLStatement {
constructor() { constructor() {
...@@ -555,24 +551,22 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -555,24 +551,22 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
return 'select ? as foo, ? as bar'; return 'select ? as foo, ? as bar';
} }
} }
return this.sequelize.query(new SQLStatement(), { type: this.sequelize.QueryTypes.SELECT, logging: s => logSql = s } ).then(result => { const result = await this.sequelize.query(new SQLStatement(), { type: this.sequelize.QueryTypes.SELECT, logging: s => logSql = s } );
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
expect(logSql).to.not.include('?'); expect(logSql).to.not.include('?');
}); });
});
it('uses properties `query` and `values` if query is tagged', function() { it('uses properties `query` and `values` if query is tagged', async function() {
let logSql; let logSql;
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT, logging(s) { logSql = s; } }).then(result => { const result = await this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT, logging(s) { logSql = s; } });
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
expect(logSql).to.not.include('?'); expect(logSql).to.not.include('?');
}); });
});
it('uses properties `query` and `bind` if query is tagged', function() { it('uses properties `query` and `bind` if query is tagged', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql; let logSql;
return this.sequelize.query({ query: `select $1${typeCast} as foo, $2${typeCast} as bar`, bind: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT, logging(s) { logSql = s; } }).then(result => { const result = await this.sequelize.query({ query: `select $1${typeCast} as foo, $2${typeCast} as bar`, bind: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT, logging(s) { logSql = s; } });
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1'); expect(logSql).to.include('$1');
...@@ -584,90 +578,86 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -584,90 +578,86 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(logSql.match(/\?/g).length).to.equal(2); expect(logSql.match(/\?/g).length).to.equal(2);
} }
}); });
});
it('dot separated attributes when doing a raw query without nest', function() { it('dot separated attributes when doing a raw query without nest', async function() {
const tickChar = dialect === 'postgres' || dialect === 'mssql' ? '"' : '`', const tickChar = dialect === 'postgres' || dialect === 'mssql' ? '"' : '`',
sql = `select 1 as ${Sequelize.Utils.addTicks('foo.bar.baz', tickChar)}`; sql = `select 1 as ${Sequelize.Utils.addTicks('foo.bar.baz', tickChar)}`;
return expect(this.sequelize.query(sql, { raw: true, nest: false }).then(obj => obj[0])).to.eventually.deep.equal([{ 'foo.bar.baz': 1 }]); await expect(this.sequelize.query(sql, { raw: true, nest: false }).then(obj => obj[0])).to.eventually.deep.equal([{ 'foo.bar.baz': 1 }]);
}); });
it('destructs dot separated attributes when doing a raw query using nest', function() { it('destructs dot separated attributes when doing a raw query using nest', async function() {
const tickChar = dialect === 'postgres' || dialect === 'mssql' ? '"' : '`', const tickChar = dialect === 'postgres' || dialect === 'mssql' ? '"' : '`',
sql = `select 1 as ${Sequelize.Utils.addTicks('foo.bar.baz', tickChar)}`; sql = `select 1 as ${Sequelize.Utils.addTicks('foo.bar.baz', tickChar)}`;
return this.sequelize.query(sql, { raw: true, nest: true }).then(result => { const result = await this.sequelize.query(sql, { raw: true, nest: true });
expect(result).to.deep.equal([{ foo: { bar: { baz: 1 } } }]); expect(result).to.deep.equal([{ foo: { bar: { baz: 1 } } }]);
}); });
});
it('replaces token with the passed array', function() { it('replaces token with the passed array', async function() {
return this.sequelize.query('select ? as foo, ? as bar', { type: this.sequelize.QueryTypes.SELECT, replacements: [1, 2] }).then(result => { const result = await this.sequelize.query('select ? as foo, ? as bar', { type: this.sequelize.QueryTypes.SELECT, replacements: [1, 2] });
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
}); });
});
it('replaces named parameters with the passed object', function() { it('replaces named parameters with the passed object', async function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0])) await expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0]))
.to.eventually.deep.equal([{ foo: 1, bar: 2 }]); .to.eventually.deep.equal([{ foo: 1, bar: 2 }]);
}); });
it('replaces named parameters with the passed object and ignore those which does not qualify', function() { it('replaces named parameters with the passed object and ignore those which does not qualify', async function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, \'00:00\' as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0])) await expect(this.sequelize.query('select :one as foo, :two as bar, \'00:00\' as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0]))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: '00:00' }]); .to.eventually.deep.equal([{ foo: 1, bar: 2, baz: '00:00' }]);
}); });
it('replaces named parameters with the passed object using the same key twice', function() { it('replaces named parameters with the passed object using the same key twice', async function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, :one as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0])) await expect(this.sequelize.query('select :one as foo, :two as bar, :one as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0]))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1 }]); .to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1 }]);
}); });
it('replaces named parameters with the passed object having a null property', function() { it('replaces named parameters with the passed object having a null property', async function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: null } }).then(obj => obj[0])) await expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: null } }).then(obj => obj[0]))
.to.eventually.deep.equal([{ foo: 1, bar: null }]); .to.eventually.deep.equal([{ foo: 1, bar: null }]);
}); });
it('reject when key is missing in the passed object', function() { it('reject when key is missing in the passed object', async function() {
return this.sequelize.query('select :one as foo, :two as bar, :three as baz', { raw: true, replacements: { one: 1, two: 2 } }) await this.sequelize.query('select :one as foo, :two as bar, :three as baz', { raw: true, replacements: { one: 1, two: 2 } })
.should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g);
}); });
it('reject with the passed number', function() { it('reject with the passed number', async function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 2 }) await this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 2 })
.should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g);
}); });
it('reject with the passed empty object', function() { it('reject with the passed empty object', async function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: {} }) await this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: {} })
.should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g);
}); });
it('reject with the passed string', function() { it('reject with the passed string', async function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 'foobar' }) await this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 'foobar' })
.should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g);
}); });
it('reject with the passed date', function() { it('reject with the passed date', async function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: new Date() }) await this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: new Date() })
.should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named parameter ":\w+" has no value in the given object\./g);
}); });
it('binds token with the passed array', function() { it('binds token with the passed array', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql; let logSql;
return this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { type: this.sequelize.QueryTypes.SELECT, bind: [1, 2], logging(s) { logSql = s;} }).then(result => { const result = await this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { type: this.sequelize.QueryTypes.SELECT, bind: [1, 2], logging(s) { logSql = s;} });
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1'); expect(logSql).to.include('$1');
} }
}); });
});
it('binds named parameters with the passed object', function() { it('binds named parameters with the passed object', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql; let logSql;
return this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar`, { raw: true, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } }).then(result => { const result = await this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar`, { raw: true, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } });
expect(result[0]).to.deep.equal([{ foo: 1, bar: 2 }]); expect(result[0]).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres') { if (dialect === 'postgres') {
expect(logSql).to.include('$1'); expect(logSql).to.include('$1');
...@@ -676,12 +666,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -676,12 +666,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(logSql).to.include('$one'); expect(logSql).to.include('$one');
} }
}); });
});
it('binds named parameters with the passed object using the same key twice', function() { it('binds named parameters with the passed object using the same key twice', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql; let logSql;
return this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar, $one${typeCast} as baz`, { raw: true, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } }).then(result => { const result = await this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar, $one${typeCast} as baz`, { raw: true, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } });
expect(result[0]).to.deep.equal([{ foo: 1, bar: 2, baz: 1 }]); expect(result[0]).to.deep.equal([{ foo: 1, bar: 2, baz: 1 }]);
if (dialect === 'postgres') { if (dialect === 'postgres') {
expect(logSql).to.include('$1'); expect(logSql).to.include('$1');
...@@ -689,141 +678,134 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -689,141 +678,134 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(logSql).to.not.include('$3'); expect(logSql).to.not.include('$3');
} }
}); });
});
it('binds named parameters with the passed object having a null property', function() { it('binds named parameters with the passed object having a null property', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
return this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar`, { raw: true, bind: { one: 1, two: null } }).then(result => { const result = await this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar`, { raw: true, bind: { one: 1, two: null } });
expect(result[0]).to.deep.equal([{ foo: 1, bar: null }]); expect(result[0]).to.deep.equal([{ foo: 1, bar: null }]);
}); });
});
it('binds named parameters array handles escaped $$', function() { it('binds named parameters array handles escaped $$', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql; let logSql;
return this.sequelize.query(`select $1${typeCast} as foo, '$$ / $$1' as bar`, { raw: true, bind: [1], logging(s) { logSql = s;} }).then(result => { const result = await this.sequelize.query(`select $1${typeCast} as foo, '$$ / $$1' as bar`, { raw: true, bind: [1], logging(s) { logSql = s;} });
expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $1' }]); expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $1' }]);
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1'); expect(logSql).to.include('$1');
} }
}); });
});
it('binds named parameters object handles escaped $$', function() { it('binds named parameters object handles escaped $$', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
return this.sequelize.query(`select $one${typeCast} as foo, '$$ / $$one' as bar`, { raw: true, bind: { one: 1 } }).then(result => { const result = await this.sequelize.query(`select $one${typeCast} as foo, '$$ / $$one' as bar`, { raw: true, bind: { one: 1 } });
expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $one' }]); expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $one' }]);
}); });
});
it('escape where has $ on the middle of characters', function() { it('escape where has $ on the middle of characters', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
return this.sequelize.query(`select $one${typeCast} as foo$bar`, { raw: true, bind: { one: 1 } }).then(result => { const result = await this.sequelize.query(`select $one${typeCast} as foo$bar`, { raw: true, bind: { one: 1 } });
expect(result[0]).to.deep.equal([{ foo$bar: 1 }]); expect(result[0]).to.deep.equal([{ foo$bar: 1 }]);
}); });
});
if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') { if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') {
it('does not improperly escape arrays of strings bound to named parameters', function() { it('does not improperly escape arrays of strings bound to named parameters', async function() {
return this.sequelize.query('select :stringArray as foo', { raw: true, replacements: { stringArray: ['"string"'] } }).then(result => { const result = await this.sequelize.query('select :stringArray as foo', { raw: true, replacements: { stringArray: ['"string"'] } });
expect(result[0]).to.deep.equal([{ foo: '"string"' }]); expect(result[0]).to.deep.equal([{ foo: '"string"' }]);
}); });
});
} }
it('reject when binds passed with object and numeric $1 is also present', function() { it('reject when binds passed with object and numeric $1 is also present', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
return this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar, '$1' as baz`, { raw: true, bind: { one: 1, two: 2 } })
await this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar, '$1' as baz`, { raw: true, bind: { one: 1, two: 2 } })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject when binds passed as array and $alpha is also present', function() { it('reject when binds passed as array and $alpha is also present', async function() {
const typeCast = dialect === 'postgres' ? '::int' : ''; const typeCast = dialect === 'postgres' ? '::int' : '';
return this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar, '$foo' as baz`, { raw: true, bind: [1, 2] })
await this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar, '$foo' as baz`, { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject when bind key is $0 with the passed array', function() { it('reject when bind key is $0 with the passed array', async function() {
return this.sequelize.query('select $1 as foo, $0 as bar, $3 as baz', { raw: true, bind: [1, 2] }) await this.sequelize.query('select $1 as foo, $0 as bar, $3 as baz', { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject when bind key is $01 with the passed array', function() { it('reject when bind key is $01 with the passed array', async function() {
return this.sequelize.query('select $1 as foo, $01 as bar, $3 as baz', { raw: true, bind: [1, 2] }) await this.sequelize.query('select $1 as foo, $01 as bar, $3 as baz', { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject when bind key is missing in the passed array', function() { it('reject when bind key is missing in the passed array', async function() {
return this.sequelize.query('select $1 as foo, $2 as bar, $3 as baz', { raw: true, bind: [1, 2] }) await this.sequelize.query('select $1 as foo, $2 as bar, $3 as baz', { raw: true, bind: [1, 2] })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject when bind key is missing in the passed object', function() { it('reject when bind key is missing in the passed object', async function() {
return this.sequelize.query('select $one as foo, $two as bar, $three as baz', { raw: true, bind: { one: 1, two: 2 } }) await this.sequelize.query('select $one as foo, $two as bar, $three as baz', { raw: true, bind: { one: 1, two: 2 } })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject with the passed number for bind', function() { it('reject with the passed number for bind', async function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 2 }) await this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 2 })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject with the passed empty object for bind', function() { it('reject with the passed empty object for bind', async function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: {} }) await this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: {} })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject with the passed string for bind', function() { it('reject with the passed string for bind', async function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 'foobar' }) await this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 'foobar' })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('reject with the passed date for bind', function() { it('reject with the passed date for bind', async function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: new Date() }) await this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: new Date() })
.should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g); .should.be.rejectedWith(Error, /Named bind parameter "\$\w+" has no value in the given object\./g);
}); });
it('handles AS in conjunction with functions just fine', function() { it('handles AS in conjunction with functions just fine', async function() {
let datetime = dialect === 'sqlite' ? 'date(\'now\')' : 'NOW()'; let datetime = dialect === 'sqlite' ? 'date(\'now\')' : 'NOW()';
if (dialect === 'mssql') { if (dialect === 'mssql') {
datetime = 'GETDATE()'; datetime = 'GETDATE()';
} }
return this.sequelize.query(`SELECT ${datetime} AS t`).then(([result]) => { const [result] = await this.sequelize.query(`SELECT ${datetime} AS t`);
expect(moment(result[0].t).isValid()).to.be.true; expect(moment(result[0].t).isValid()).to.be.true;
}); });
});
if (Support.getTestDialect() === 'postgres') { if (Support.getTestDialect() === 'postgres') {
it('replaces named parameters with the passed object and ignores casts', function() { it('replaces named parameters with the passed object and ignores casts', async function() {
return expect(this.sequelize.query('select :one as foo, :two as bar, \'1000\'::integer as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0])) await expect(this.sequelize.query('select :one as foo, :two as bar, \'1000\'::integer as baz', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0]))
.to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1000 }]); .to.eventually.deep.equal([{ foo: 1, bar: 2, baz: 1000 }]);
}); });
it('supports WITH queries', function() { it('supports WITH queries', async function() {
return expect(this.sequelize.query('WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100) SELECT sum(n) FROM t').then(obj => obj[0])) await expect(this.sequelize.query('WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100) SELECT sum(n) FROM t').then(obj => obj[0]))
.to.eventually.deep.equal([{ 'sum': '5050' }]); .to.eventually.deep.equal([{ 'sum': '5050' }]);
}); });
} }
if (Support.getTestDialect() === 'sqlite') { if (Support.getTestDialect() === 'sqlite') {
it('binds array parameters for upsert are replaced. $$ unescapes only once', function() { it('binds array parameters for upsert are replaced. $$ unescapes only once', async function() {
let logSql; let logSql;
return this.sequelize.query('select $1 as foo, $2 as bar, \'$$$$\' as baz', { type: this.sequelize.QueryTypes.UPSERT, bind: [1, 2], logging(s) { logSql = s; } }).then(() => { await this.sequelize.query('select $1 as foo, $2 as bar, \'$$$$\' as baz', { type: this.sequelize.QueryTypes.UPSERT, bind: [1, 2], logging(s) { logSql = s; } });
// sqlite.exec does not return a result // sqlite.exec does not return a result
expect(logSql).to.not.include('$one'); expect(logSql).to.not.include('$one');
expect(logSql).to.include('\'$$\''); expect(logSql).to.include('\'$$\'');
}); });
});
it('binds named parameters for upsert are replaced. $$ unescapes only once', function() { it('binds named parameters for upsert are replaced. $$ unescapes only once', async function() {
let logSql; let logSql;
return this.sequelize.query('select $one as foo, $two as bar, \'$$$$\' as baz', { type: this.sequelize.QueryTypes.UPSERT, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } }).then(() => { await this.sequelize.query('select $one as foo, $two as bar, \'$$$$\' as baz', { type: this.sequelize.QueryTypes.UPSERT, bind: { one: 1, two: 2 }, logging(s) { logSql = s; } });
// sqlite.exec does not return a result // sqlite.exec does not return a result
expect(logSql).to.not.include('$one'); expect(logSql).to.not.include('$one');
expect(logSql).to.include('\'$$\''); expect(logSql).to.include('\'$$\'');
}); });
});
} }
}); });
...@@ -892,34 +874,30 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -892,34 +874,30 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
.to.be.rejectedWith(TypeError, 'options.transaction is required'); .to.be.rejectedWith(TypeError, 'options.transaction is required');
}); });
it('one value', function() { it('one value', async function() {
return this.sequelize.transaction().then(t => { const t = await this.sequelize.transaction();
this.t = t; this.t = t;
return this.sequelize.set({ foo: 'bar' }, { transaction: t }); await this.sequelize.set({ foo: 'bar' }, { transaction: t });
}).then(() => { const data = await this.sequelize.query('SELECT @foo as `foo`', { plain: true, transaction: this.t });
return this.sequelize.query('SELECT @foo as `foo`', { plain: true, transaction: this.t });
}).then(data => {
expect(data).to.be.ok; expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar'); expect(data.foo).to.be.equal('bar');
return this.t.commit(); await this.t.commit();
});
}); });
it('multiple values', function() { it('multiple values', async function() {
return this.sequelize.transaction().then(t => { const t = await this.sequelize.transaction();
this.t = t; this.t = t;
return this.sequelize.set({
await this.sequelize.set({
foo: 'bar', foo: 'bar',
foos: 'bars' foos: 'bars'
}, { transaction: t }); }, { transaction: t });
}).then(() => {
return this.sequelize.query('SELECT @foo as `foo`, @foos as `foos`', { plain: true, transaction: this.t }); const data = await this.sequelize.query('SELECT @foo as `foo`, @foos as `foos`', { plain: true, transaction: this.t });
}).then(data => {
expect(data).to.be.ok; expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar'); expect(data.foo).to.be.equal('bar');
expect(data.foos).to.be.equal('bars'); expect(data.foos).to.be.equal('bars');
return this.t.commit(); await this.t.commit();
});
}); });
}); });
} }
...@@ -961,21 +939,19 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -961,21 +939,19 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(DAO.options.rowFormat).to.equal('default'); expect(DAO.options.rowFormat).to.equal('default');
}); });
it('uses the passed tableName', function() { it('uses the passed tableName', async function() {
const Photo = this.sequelize.define('Foto', { name: DataTypes.STRING }, { tableName: 'photos' }); const Photo = this.sequelize.define('Foto', { name: DataTypes.STRING }, { tableName: 'photos' });
return Photo.sync({ force: true }).then(() => { await Photo.sync({ force: true });
return this.sequelize.getQueryInterface().showAllTables().then(tableNames => { let tableNames = await this.sequelize.getQueryInterface().showAllTables();
if (dialect === 'mssql' || dialect === 'mariadb') { if (dialect === 'mssql' || dialect === 'mariadb') {
tableNames = tableNames.map(v => v.tableName); tableNames = tableNames.map(v => v.tableName);
} }
expect(tableNames).to.include('photos'); expect(tableNames).to.include('photos');
}); });
}); });
});
});
describe('truncate', () => { describe('truncate', () => {
it('truncates all models', function() { it('truncates all models', async function() {
const Project = this.sequelize.define(`project${config.rand()}`, { const Project = this.sequelize.define(`project${config.rand()}`, {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
...@@ -985,47 +961,38 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -985,47 +961,38 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
title: DataTypes.STRING title: DataTypes.STRING
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Project.create({ title: 'bla' }); const project = await Project.create({ title: 'bla' });
}).then(project => {
expect(project).to.exist; expect(project).to.exist;
expect(project.title).to.equal('bla'); expect(project.title).to.equal('bla');
expect(project.id).to.equal(1); expect(project.id).to.equal(1);
return this.sequelize.truncate().then(() => { await this.sequelize.truncate();
return Project.findAll({}); const projects = await Project.findAll({});
});
}).then(projects => {
expect(projects).to.exist; expect(projects).to.exist;
expect(projects).to.have.length(0); expect(projects).to.have.length(0);
}); });
}); });
});
describe('sync', () => { describe('sync', () => {
it('synchronizes all models', function() { it('synchronizes all models', async function() {
const Project = this.sequelize.define(`project${config.rand()}`, { title: DataTypes.STRING }); const Project = this.sequelize.define(`project${config.rand()}`, { title: DataTypes.STRING });
const Task = this.sequelize.define(`task${config.rand()}`, { title: DataTypes.STRING }); const Task = this.sequelize.define(`task${config.rand()}`, { title: DataTypes.STRING });
return Project.sync({ force: true }).then(() => { await Project.sync({ force: true });
return Task.sync({ force: true }).then(() => { await Task.sync({ force: true });
return Project.create({ title: 'bla' }).then(() => { await Project.create({ title: 'bla' });
return Task.create({ title: 'bla' }).then(task => { const task = await Task.create({ title: 'bla' });
expect(task).to.exist; expect(task).to.exist;
expect(task.title).to.equal('bla'); expect(task.title).to.equal('bla');
}); });
});
});
});
});
it('works with correct database credentials', function() { it('works with correct database credentials', async function() {
const User = this.sequelize.define('User', { username: DataTypes.STRING }); const User = this.sequelize.define('User', { username: DataTypes.STRING });
return User.sync().then(() => { await User.sync();
expect(true).to.be.true; expect(true).to.be.true;
}); });
});
it('fails with incorrect match condition', function() { it('fails with incorrect match condition', async function() {
const sequelize = new Sequelize('cyber_bird', 'user', 'pass', { const sequelize = new Sequelize('cyber_bird', 'user', 'pass', {
dialect: this.sequelize.options.dialect dialect: this.sequelize.options.dialect
}); });
...@@ -1033,27 +1000,28 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1033,27 +1000,28 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { title: Sequelize.STRING }); sequelize.define('Project', { title: Sequelize.STRING });
sequelize.define('Task', { title: Sequelize.STRING }); sequelize.define('Task', { title: Sequelize.STRING });
return expect(sequelize.sync({ force: true, match: /$phoenix/ })) await expect(sequelize.sync({ force: true, match: /$phoenix/ }))
.to.be.rejectedWith('Database "cyber_bird" does not match sync match parameter "/$phoenix/"'); .to.be.rejectedWith('Database "cyber_bird" does not match sync match parameter "/$phoenix/"');
}); });
if (dialect !== 'sqlite') { if (dialect !== 'sqlite') {
it('fails for incorrect connection even when no models are defined', function() { it('fails for incorrect connection even when no models are defined', async function() {
const sequelize = new Sequelize('cyber_bird', 'user', 'pass', { const sequelize = new Sequelize('cyber_bird', 'user', 'pass', {
dialect: this.sequelize.options.dialect dialect: this.sequelize.options.dialect
}); });
return expect(sequelize.sync({ force: true })).to.be.rejected; await expect(sequelize.sync({ force: true })).to.be.rejected;
}); });
it('fails with incorrect database credentials (1)', function() { it('fails with incorrect database credentials (1)', async function() {
this.sequelizeWithInvalidCredentials = new Sequelize('omg', 'bar', null, _.omit(this.sequelize.options, ['host'])); this.sequelizeWithInvalidCredentials = new Sequelize('omg', 'bar', null, _.omit(this.sequelize.options, ['host']));
const User2 = this.sequelizeWithInvalidCredentials.define('User', { name: DataTypes.STRING, bio: DataTypes.TEXT }); const User2 = this.sequelizeWithInvalidCredentials.define('User', { name: DataTypes.STRING, bio: DataTypes.TEXT });
return User2.sync() try {
.then(() => { expect.fail(); }) await User2.sync();
.catch(err => { expect.fail();
} catch (err) {
if (dialect === 'postgres' || dialect === 'postgres-native') { if (dialect === 'postgres' || dialect === 'postgres-native') {
assert([ assert([
'fe_sendauth: no password supplied', 'fe_sendauth: no password supplied',
...@@ -1066,10 +1034,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1066,10 +1034,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
} else { } else {
expect(err.message.toString()).to.match(/.*Access denied.*/); expect(err.message.toString()).to.match(/.*Access denied.*/);
} }
}); }
}); });
it('fails with incorrect database credentials (2)', function() { it('fails with incorrect database credentials (2)', async function() {
const sequelize = new Sequelize('db', 'user', 'pass', { const sequelize = new Sequelize('db', 'user', 'pass', {
dialect: this.sequelize.options.dialect dialect: this.sequelize.options.dialect
}); });
...@@ -1077,10 +1045,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1077,10 +1045,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { title: Sequelize.STRING }); sequelize.define('Project', { title: Sequelize.STRING });
sequelize.define('Task', { title: Sequelize.STRING }); sequelize.define('Task', { title: Sequelize.STRING });
return expect(sequelize.sync({ force: true })).to.be.rejected; await expect(sequelize.sync({ force: true })).to.be.rejected;
}); });
it('fails with incorrect database credentials (3)', function() { it('fails with incorrect database credentials (3)', async function() {
const sequelize = new Sequelize('db', 'user', 'pass', { const sequelize = new Sequelize('db', 'user', 'pass', {
dialect: this.sequelize.options.dialect, dialect: this.sequelize.options.dialect,
port: 99999 port: 99999
...@@ -1089,10 +1057,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1089,10 +1057,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { title: Sequelize.STRING }); sequelize.define('Project', { title: Sequelize.STRING });
sequelize.define('Task', { title: Sequelize.STRING }); sequelize.define('Task', { title: Sequelize.STRING });
return expect(sequelize.sync({ force: true })).to.be.rejected; await expect(sequelize.sync({ force: true })).to.be.rejected;
}); });
it('fails with incorrect database credentials (4)', function() { it('fails with incorrect database credentials (4)', async function() {
const sequelize = new Sequelize('db', 'user', 'pass', { const sequelize = new Sequelize('db', 'user', 'pass', {
dialect: this.sequelize.options.dialect, dialect: this.sequelize.options.dialect,
port: 99999, port: 99999,
...@@ -1102,10 +1070,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1102,10 +1070,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { title: Sequelize.STRING }); sequelize.define('Project', { title: Sequelize.STRING });
sequelize.define('Task', { title: Sequelize.STRING }); sequelize.define('Task', { title: Sequelize.STRING });
return expect(sequelize.sync({ force: true })).to.be.rejected; await expect(sequelize.sync({ force: true })).to.be.rejected;
}); });
it('returns an error correctly if unable to sync a foreign key referenced model', function() { it('returns an error correctly if unable to sync a foreign key referenced model', async function() {
this.sequelize.define('Application', { this.sequelize.define('Application', {
authorID: { authorID: {
type: Sequelize.BIGINT, type: Sequelize.BIGINT,
...@@ -1117,10 +1085,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1117,10 +1085,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
} }
}); });
return expect(this.sequelize.sync()).to.be.rejected; await expect(this.sequelize.sync()).to.be.rejected;
}); });
it('handles this dependant foreign key constraints', function() { it('handles this dependant foreign key constraints', async function() {
const block = this.sequelize.define('block', { const block = this.sequelize.define('block', {
id: { type: DataTypes.INTEGER, primaryKey: true }, id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING name: DataTypes.STRING
...@@ -1145,17 +1113,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1145,17 +1113,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
foreignKeyConstraint: true foreignKeyConstraint: true
}); });
return this.sequelize.sync(); await this.sequelize.sync();
}); });
} }
it('return the sequelize instance after syncing', function() { it('return the sequelize instance after syncing', async function() {
return this.sequelize.sync().then(sequelize => { const sequelize = await this.sequelize.sync();
expect(sequelize).to.deep.equal(this.sequelize); expect(sequelize).to.deep.equal(this.sequelize);
}); });
});
it('return the single dao after syncing', function() { it('return the single dao after syncing', async function() {
const block = this.sequelize.define('block', { const block = this.sequelize.define('block', {
id: { type: DataTypes.INTEGER, primaryKey: true }, id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING name: DataTypes.STRING
...@@ -1165,12 +1132,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1165,12 +1132,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
paranoid: false paranoid: false
}); });
return block.sync().then(result => { const result = await block.sync();
expect(result).to.deep.equal(block); expect(result).to.deep.equal(block);
}); });
});
it('handles alter: true with underscore correctly', function() { it('handles alter: true with underscore correctly', async function() {
this.sequelize.define('access_metric', { this.sequelize.define('access_metric', {
user_id: { user_id: {
type: DataTypes.INTEGER type: DataTypes.INTEGER
...@@ -1179,7 +1145,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1179,7 +1145,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
underscored: true underscored: true
}); });
return this.sequelize.sync({ await this.sequelize.sync({
alter: true alter: true
}); });
}); });
...@@ -1195,20 +1161,18 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1195,20 +1161,18 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.User = this.sequelize.define('UserTest', { username: DataTypes.STRING }); this.User = this.sequelize.define('UserTest', { username: DataTypes.STRING });
}); });
it('through Sequelize.sync()', function() { it('through Sequelize.sync()', async function() {
this.spy.resetHistory(); this.spy.resetHistory();
return this.sequelize.sync({ force: true, logging: false }).then(() => { await this.sequelize.sync({ force: true, logging: false });
expect(this.spy.notCalled).to.be.true; expect(this.spy.notCalled).to.be.true;
}); });
});
it('through DAOFactory.sync()', function() { it('through DAOFactory.sync()', async function() {
this.spy.resetHistory(); this.spy.resetHistory();
return this.User.sync({ force: true, logging: false }).then(() => { await this.User.sync({ force: true, logging: false });
expect(this.spy.notCalled).to.be.true; expect(this.spy.notCalled).to.be.true;
}); });
}); });
});
describe('match', () => { describe('match', () => {
it('will return an error not matching', function() { it('will return an error not matching', function() {
...@@ -1223,11 +1187,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1223,11 +1187,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}); });
describe('drop should work', () => { describe('drop should work', () => {
it('correctly succeeds', function() { it('correctly succeeds', async function() {
const User = this.sequelize.define('Users', { username: DataTypes.STRING }); const User = this.sequelize.define('Users', { username: DataTypes.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return User.drop(); await User.drop();
});
}); });
}); });
...@@ -1247,13 +1210,13 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1247,13 +1210,13 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
DataTypes.ENUM('scheduled', 'active', 'finished') DataTypes.ENUM('scheduled', 'active', 'finished')
].forEach(status => { ].forEach(status => {
describe('enum', () => { describe('enum', () => {
beforeEach(function() { beforeEach(async function() {
this.sequelize = Support.createSequelizeInstance({ this.sequelize = Support.createSequelizeInstance({
typeValidation: true typeValidation: true
}); });
this.Review = this.sequelize.define('review', { status }); this.Review = this.sequelize.define('review', { status });
return this.Review.sync({ force: true }); await this.Review.sync({ force: true });
}); });
it('raises an error if no values are defined', function() { it('raises an error if no values are defined', function() {
...@@ -1264,25 +1227,24 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1264,25 +1227,24 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}).to.throw(Error, 'Values for ENUM have not been defined.'); }).to.throw(Error, 'Values for ENUM have not been defined.');
}); });
it('correctly stores values', function() { it('correctly stores values', async function() {
return this.Review.create({ status: 'active' }).then(review => { const review = await this.Review.create({ status: 'active' });
expect(review.status).to.equal('active'); expect(review.status).to.equal('active');
}); });
});
it('correctly loads values', function() { it('correctly loads values', async function() {
return this.Review.create({ status: 'active' }).then(() => { await this.Review.create({ status: 'active' });
return this.Review.findAll().then(reviews => { const reviews = await this.Review.findAll();
expect(reviews[0].status).to.equal('active'); expect(reviews[0].status).to.equal('active');
}); });
});
});
it("doesn't save an instance if value is not in the range of enums", function() { it("doesn't save an instance if value is not in the range of enums", async function() {
return this.Review.create({ status: 'fnord' }).catch(err => { try {
await this.Review.create({ status: 'fnord' });
} catch (err) {
expect(err).to.be.instanceOf(Error); expect(err).to.be.instanceOf(Error);
expect(err.message).to.equal('"fnord" is not a valid choice in ["scheduled","active","finished"]'); expect(err.message).to.equal('"fnord" is not a valid choice in ["scheduled","active","finished"]');
}); }
}); });
}); });
}); });
...@@ -1294,9 +1256,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1294,9 +1256,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
{ id: { type: DataTypes.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } } { id: { type: DataTypes.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].forEach(customAttributes => { ].forEach(customAttributes => {
it('should be able to override options on the default attributes', function() { it('should be able to override options on the default attributes', async function() {
const Picture = this.sequelize.define('picture', _.cloneDeep(customAttributes)); const Picture = this.sequelize.define('picture', _.cloneDeep(customAttributes));
return Picture.sync({ force: true }).then(() => { await Picture.sync({ force: true });
Object.keys(customAttributes).forEach(attribute => { Object.keys(customAttributes).forEach(attribute => {
Object.keys(customAttributes[attribute]).forEach(option => { Object.keys(customAttributes[attribute]).forEach(option => {
const optionValue = customAttributes[attribute][option]; const optionValue = customAttributes[attribute][option];
...@@ -1308,243 +1270,187 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1308,243 +1270,187 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}); });
}); });
}); });
});
}); });
}); });
if (current.dialect.supports.transactions) { if (current.dialect.supports.transactions) {
describe('transaction', () => { describe('transaction', () => {
beforeEach(function() { beforeEach(async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelizeWithTransaction = sequelize; this.sequelizeWithTransaction = sequelize;
}); });
});
it('is a transaction method available', () => { it('is a transaction method available', () => {
expect(Support.Sequelize).to.respondTo('transaction'); expect(Support.Sequelize).to.respondTo('transaction');
}); });
it('passes a transaction object to the callback', function() { it('passes a transaction object to the callback', async function() {
return this.sequelizeWithTransaction.transaction().then(t => { const t = await this.sequelizeWithTransaction.transaction();
expect(t).to.be.instanceOf(Transaction); expect(t).to.be.instanceOf(Transaction);
}); });
});
it('allows me to define a callback on the result', function() { it('allows me to define a callback on the result', async function() {
return this.sequelizeWithTransaction.transaction().then(t => { const t = await this.sequelizeWithTransaction.transaction();
return t.commit(); await t.commit();
});
}); });
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('correctly scopes transaction from other connections', function() { it('correctly scopes transaction from other connections', async function() {
const TransactionTest = this.sequelizeWithTransaction.define('TransactionTest', { name: DataTypes.STRING }, { timestamps: false }); const TransactionTest = this.sequelizeWithTransaction.define('TransactionTest', { name: DataTypes.STRING }, { timestamps: false });
const count = transaction => { const count = async transaction => {
const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] }); const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] });
return this.sequelizeWithTransaction.query(sql, { plain: true, transaction }).then(result => { const result = await this.sequelizeWithTransaction.query(sql, { plain: true, transaction });
return result.cnt; return result.cnt;
});
}; };
return TransactionTest.sync({ force: true }).then(() => { await TransactionTest.sync({ force: true });
return this.sequelizeWithTransaction.transaction(); const t1 = await this.sequelizeWithTransaction.transaction();
}).then(t1 => {
this.t1 = t1; this.t1 = t1;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 }); await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
}).then(() => { await expect(count()).to.eventually.equal(0);
return expect(count()).to.eventually.equal(0); await expect(count(this.t1)).to.eventually.equal(1);
}).then(() => { await this.t1.commit();
return expect(count(this.t1)).to.eventually.equal(1);
}).then(() => { await expect(count()).to.eventually.equal(1);
return this.t1.commit();
}).then(() => {
return expect(count()).to.eventually.equal(1);
});
}); });
} else { } else {
it('correctly handles multiple transactions', function() { it('correctly handles multiple transactions', async function() {
const TransactionTest = this.sequelizeWithTransaction.define('TransactionTest', { name: DataTypes.STRING }, { timestamps: false }); const TransactionTest = this.sequelizeWithTransaction.define('TransactionTest', { name: DataTypes.STRING }, { timestamps: false });
const aliasesMapping = new Map([['_0', 'cnt']]); const aliasesMapping = new Map([['_0', 'cnt']]);
const count = transaction => { const count = async transaction => {
const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] }); const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] });
return this.sequelizeWithTransaction.query(sql, { plain: true, transaction, aliasesMapping }).then(result => { const result = await this.sequelizeWithTransaction.query(sql, { plain: true, transaction, aliasesMapping });
return parseInt(result.cnt, 10); return parseInt(result.cnt, 10);
});
}; };
return TransactionTest.sync({ force: true }).then(() => { await TransactionTest.sync({ force: true });
return this.sequelizeWithTransaction.transaction(); const t1 = await this.sequelizeWithTransaction.transaction();
}).then(t1 => {
this.t1 = t1; this.t1 = t1;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 }); await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
}).then(() => { const t2 = await this.sequelizeWithTransaction.transaction();
return this.sequelizeWithTransaction.transaction();
}).then(t2 => {
this.t2 = t2; this.t2 = t2;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('bar');`, { transaction: t2 }); await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('bar');`, { transaction: t2 });
}).then(() => { await expect(count()).to.eventually.equal(0);
return expect(count()).to.eventually.equal(0); await expect(count(this.t1)).to.eventually.equal(1);
}).then(() => { await expect(count(this.t2)).to.eventually.equal(1);
return expect(count(this.t1)).to.eventually.equal(1); await this.t2.rollback();
}).then(() => { await expect(count()).to.eventually.equal(0);
return expect(count(this.t2)).to.eventually.equal(1); await this.t1.commit();
}).then(() => {
await expect(count()).to.eventually.equal(1);
return this.t2.rollback();
}).then(() => {
return expect(count()).to.eventually.equal(0);
}).then(() => {
return this.t1.commit();
}).then(() => {
return expect(count()).to.eventually.equal(1);
});
}); });
} }
it('supports nested transactions using savepoints', function() { it('supports nested transactions using savepoints', async function() {
const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING }); const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return this.sequelizeWithTransaction.transaction().then(t1 => { const t1 = await this.sequelizeWithTransaction.transaction();
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => { const user = await User.create({ username: 'foo' }, { transaction: t1 });
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => { const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => { await user.update({ username: 'bar' }, { transaction: t2 });
return t2.commit().then(() => { await t2.commit();
return user.reload({ transaction: t1 }).then(newUser => { const newUser = await user.reload({ transaction: t1 });
expect(newUser.username).to.equal('bar'); expect(newUser.username).to.equal('bar');
return t1.commit();
}); await t1.commit();
});
});
});
});
});
});
}); });
describe('supports rolling back to savepoints', () => { describe('supports rolling back to savepoints', () => {
beforeEach(function() { beforeEach(async function() {
this.User = this.sequelizeWithTransaction.define('user', {}); this.User = this.sequelizeWithTransaction.define('user', {});
return this.sequelizeWithTransaction.sync({ force: true }); await this.sequelizeWithTransaction.sync({ force: true });
}); });
it('rolls back to the first savepoint, undoing everything', function() { it('rolls back to the first savepoint, undoing everything', async function() {
return this.sequelizeWithTransaction.transaction().then(transaction => { const transaction = await this.sequelizeWithTransaction.transaction();
this.transaction = transaction; this.transaction = transaction;
return this.sequelizeWithTransaction.transaction({ transaction }); const sp1 = await this.sequelizeWithTransaction.transaction({ transaction });
}).then(sp1 => {
this.sp1 = sp1; this.sp1 = sp1;
return this.User.create({}, { transaction: this.transaction }); await this.User.create({}, { transaction: this.transaction });
}).then(() => { const sp2 = await this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
return this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
}).then(sp2 => {
this.sp2 = sp2; this.sp2 = sp2;
return this.User.create({}, { transaction: this.transaction }); await this.User.create({}, { transaction: this.transaction });
}).then(() => { const users0 = await this.User.findAll({ transaction: this.transaction });
return this.User.findAll({ transaction: this.transaction }); expect(users0).to.have.length(2);
}).then(users => {
expect(users).to.have.length(2); await this.sp1.rollback();
const users = await this.User.findAll({ transaction: this.transaction });
return this.sp1.rollback();
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(0); expect(users).to.have.length(0);
return this.transaction.rollback(); await this.transaction.rollback();
});
}); });
it('rolls back to the most recent savepoint, only undoing recent changes', function() { it('rolls back to the most recent savepoint, only undoing recent changes', async function() {
return this.sequelizeWithTransaction.transaction().then(transaction => { const transaction = await this.sequelizeWithTransaction.transaction();
this.transaction = transaction; this.transaction = transaction;
return this.sequelizeWithTransaction.transaction({ transaction }); const sp1 = await this.sequelizeWithTransaction.transaction({ transaction });
}).then(sp1 => {
this.sp1 = sp1; this.sp1 = sp1;
return this.User.create({}, { transaction: this.transaction }); await this.User.create({}, { transaction: this.transaction });
}).then(() => { const sp2 = await this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
return this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
}).then(sp2 => {
this.sp2 = sp2; this.sp2 = sp2;
return this.User.create({}, { transaction: this.transaction }); await this.User.create({}, { transaction: this.transaction });
}).then(() => { const users0 = await this.User.findAll({ transaction: this.transaction });
return this.User.findAll({ transaction: this.transaction }); expect(users0).to.have.length(2);
}).then(users => {
expect(users).to.have.length(2); await this.sp2.rollback();
const users = await this.User.findAll({ transaction: this.transaction });
return this.sp2.rollback();
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(1); expect(users).to.have.length(1);
return this.transaction.rollback(); await this.transaction.rollback();
});
}); });
}); });
it('supports rolling back a nested transaction', function() { it('supports rolling back a nested transaction', async function() {
const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING }); const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return this.sequelizeWithTransaction.transaction().then(t1 => { const t1 = await this.sequelizeWithTransaction.transaction();
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => { const user = await User.create({ username: 'foo' }, { transaction: t1 });
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => { const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => { await user.update({ username: 'bar' }, { transaction: t2 });
return t2.rollback().then(() => { await t2.rollback();
return user.reload({ transaction: t1 }).then(newUser => { const newUser = await user.reload({ transaction: t1 });
expect(newUser.username).to.equal('foo'); expect(newUser.username).to.equal('foo');
return t1.commit();
}); await t1.commit();
});
});
});
});
});
});
}); });
it('supports rolling back outermost transaction', function() { it('supports rolling back outermost transaction', async function() {
const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING }); const User = this.sequelizeWithTransaction.define('Users', { username: DataTypes.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return this.sequelizeWithTransaction.transaction().then(t1 => { const t1 = await this.sequelizeWithTransaction.transaction();
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => { const user = await User.create({ username: 'foo' }, { transaction: t1 });
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => { const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => { await user.update({ username: 'bar' }, { transaction: t2 });
return t1.rollback().then(() => { await t1.rollback();
return User.findAll().then(users => { const users = await User.findAll();
expect(users.length).to.equal(0); expect(users.length).to.equal(0);
}); });
}); });
});
});
});
});
});
});
});
} }
}); });
describe('databaseVersion', () => { describe('databaseVersion', () => {
it('should database/dialect version', function() { it('should database/dialect version', async function() {
return this.sequelize.databaseVersion().then(version => { const version = await this.sequelize.databaseVersion();
expect(typeof version).to.equal('string'); expect(typeof version).to.equal('string');
expect(version).to.be.ok; expect(version).to.be.ok;
}); });
}); });
});
describe('paranoid deletedAt non-null default value', () => { describe('paranoid deletedAt non-null default value', () => {
it('should use defaultValue of deletedAt in paranoid clause and restore', function() { it('should use defaultValue of deletedAt in paranoid clause and restore', async function() {
const epochObj = new Date(0), const epochObj = new Date(0),
epoch = Number(epochObj); epoch = Number(epochObj);
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
...@@ -1557,45 +1463,39 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -1557,45 +1463,39 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
paranoid: true paranoid: true
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({ username: 'user1' }).then(user => { const user = await User.create({ username: 'user1' });
expect(Number(user.deletedAt)).to.equal(epoch); expect(Number(user.deletedAt)).to.equal(epoch);
return User.findOne({
const user0 = await User.findOne({
where: { where: {
username: 'user1' username: 'user1'
} }
}).then(user => { });
expect(user).to.exist;
expect(Number(user.deletedAt)).to.equal(epoch); expect(user0).to.exist;
return user.destroy(); expect(Number(user0.deletedAt)).to.equal(epoch);
}).then(destroyedUser => { const destroyedUser = await user0.destroy();
expect(destroyedUser.deletedAt).to.exist; expect(destroyedUser.deletedAt).to.exist;
expect(Number(destroyedUser.deletedAt)).not.to.equal(epoch); expect(Number(destroyedUser.deletedAt)).not.to.equal(epoch);
return User.findByPk(destroyedUser.id, { paranoid: false }); const fetchedDestroyedUser = await User.findByPk(destroyedUser.id, { paranoid: false });
}).then(fetchedDestroyedUser => {
expect(fetchedDestroyedUser.deletedAt).to.exist; expect(fetchedDestroyedUser.deletedAt).to.exist;
expect(Number(fetchedDestroyedUser.deletedAt)).not.to.equal(epoch); expect(Number(fetchedDestroyedUser.deletedAt)).not.to.equal(epoch);
return fetchedDestroyedUser.restore(); const restoredUser = await fetchedDestroyedUser.restore();
}).then(restoredUser => {
expect(Number(restoredUser.deletedAt)).to.equal(epoch); expect(Number(restoredUser.deletedAt)).to.equal(epoch);
return User.destroy({ where: {
await User.destroy({ where: {
username: 'user1' username: 'user1'
} }); } });
}).then(() => {
return User.count(); const count = await User.count();
}).then(count => {
expect(count).to.equal(0); expect(count).to.equal(0);
return User.restore(); await User.restore();
}).then(() => { const nonDeletedUsers = await User.findAll();
return User.findAll();
}).then(nonDeletedUsers => {
expect(nonDeletedUsers.length).to.equal(1); expect(nonDeletedUsers.length).to.equal(1);
nonDeletedUsers.forEach(u => { nonDeletedUsers.forEach(u => {
expect(Number(u.deletedAt)).to.equal(epoch); expect(Number(u.deletedAt)).to.equal(epoch);
}); });
}); });
}); });
});
});
});
}); });
...@@ -12,47 +12,41 @@ if (current.dialect.supports.transactions) { ...@@ -12,47 +12,41 @@ if (current.dialect.supports.transactions) {
describe(Support.getTestDialectTeaser('Sequelize#transaction'), () => { describe(Support.getTestDialectTeaser('Sequelize#transaction'), () => {
describe('then', () => { describe('then', () => {
it('gets triggered once a transaction has been successfully committed', function() { it('gets triggered once a transaction has been successfully committed', async function() {
let called = false; let called = false;
return this
const t = await this
.sequelize .sequelize
.transaction().then(t => { .transaction();
return t.commit().then(() => {
await t.commit();
called = 1; called = 1;
});
})
.then(() => {
expect(called).to.be.ok; expect(called).to.be.ok;
}); });
});
it('gets triggered once a transaction has been successfully rolled back', function() { it('gets triggered once a transaction has been successfully rolled back', async function() {
let called = false; let called = false;
return this
const t = await this
.sequelize .sequelize
.transaction().then(t => { .transaction();
return t.rollback().then(() => {
await t.rollback();
called = 1; called = 1;
});
})
.then(() => {
expect(called).to.be.ok; expect(called).to.be.ok;
}); });
});
if (Support.getTestDialect() !== 'sqlite') { if (Support.getTestDialect() !== 'sqlite') {
it('works for long running transactions', function() { it('works for long running transactions', async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelize = sequelize; this.sequelize = sequelize;
this.User = sequelize.define('User', { this.User = sequelize.define('User', {
name: Support.Sequelize.STRING name: Support.Sequelize.STRING
}, { timestamps: false }); }, { timestamps: false });
return sequelize.sync({ force: true }); await sequelize.sync({ force: true });
}).then(() => { const t = await this.sequelize.transaction();
return this.sequelize.transaction();
}).then(t => {
let query = 'select sleep(2);'; let query = 'select sleep(2);';
switch (Support.getTestDialect()) { switch (Support.getTestDialect()) {
...@@ -69,57 +63,46 @@ if (current.dialect.supports.transactions) { ...@@ -69,57 +63,46 @@ if (current.dialect.supports.transactions) {
break; break;
} }
return this.sequelize.query(query, { transaction: t }).then(() => { await this.sequelize.query(query, { transaction: t });
return this.User.create({ name: 'foo' }); await this.User.create({ name: 'foo' });
}).then(() => { await this.sequelize.query(query, { transaction: t });
return this.sequelize.query(query, { transaction: t }); await t.commit();
}).then(() => { const users = await this.User.findAll();
return t.commit();
});
}).then(() => {
return this.User.findAll();
}).then(users => {
expect(users.length).to.equal(1); expect(users.length).to.equal(1);
expect(users[0].name).to.equal('foo'); expect(users[0].name).to.equal('foo');
}); });
});
} }
}); });
describe('complex long running example', () => { describe('complex long running example', () => {
it('works with promise syntax', function() { it('works with promise syntax', async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
const Test = sequelize.define('Test', { const Test = sequelize.define('Test', {
id: { type: Support.Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, id: { type: Support.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: Support.Sequelize.STRING } name: { type: Support.Sequelize.STRING }
}); });
return sequelize.sync({ force: true }).then(() => { await sequelize.sync({ force: true });
return sequelize.transaction().then(transaction => { const transaction = await sequelize.transaction();
expect(transaction).to.be.instanceOf(Transaction); expect(transaction).to.be.instanceOf(Transaction);
return Test await Test
.create({ name: 'Peter' }, { transaction }) .create({ name: 'Peter' }, { transaction });
.then(() => {
return delay(1000).then(() => { await delay(1000);
return transaction
.commit() await transaction
.then(() => { return Test.count(); }) .commit();
.then(count => {
const count = await Test.count();
expect(count).to.equal(1); expect(count).to.equal(1);
}); });
}); });
});
});
});
});
});
});
describe('concurrency', () => { describe('concurrency', () => {
describe('having tables with uniqueness constraints', () => { describe('having tables with uniqueness constraints', () => {
beforeEach(function() { beforeEach(async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelize = sequelize; this.sequelize = sequelize;
this.Model = sequelize.define('Model', { this.Model = sequelize.define('Model', {
...@@ -128,19 +111,23 @@ if (current.dialect.supports.transactions) { ...@@ -128,19 +111,23 @@ if (current.dialect.supports.transactions) {
timestamps: false timestamps: false
}); });
return this.Model.sync({ force: true }); await this.Model.sync({ force: true });
});
}); });
it('triggers the error event for the second transactions', function() { it('triggers the error event for the second transactions', async function() {
return this.sequelize.transaction().then(t1 => { const t1 = await this.sequelize.transaction();
return this.sequelize.transaction().then(t2 => { const t2 = await this.sequelize.transaction();
return this.Model.create({ name: 'omnom' }, { transaction: t1 }).then(() => { await this.Model.create({ name: 'omnom' }, { transaction: t1 });
return Promise.all([
this.Model.create({ name: 'omnom' }, { transaction: t2 }).catch(err => { await Promise.all([
(async () => {
try {
return await this.Model.create({ name: 'omnom' }, { transaction: t2 });
} catch (err) {
expect(err).to.be.ok; expect(err).to.be.ok;
return t2.rollback(); return t2.rollback();
}), }
})(),
delay(100).then(() => { delay(100).then(() => {
return t1.commit(); return t1.commit();
}) })
...@@ -149,8 +136,5 @@ if (current.dialect.supports.transactions) { ...@@ -149,8 +136,5 @@ if (current.dialect.supports.transactions) {
}); });
}); });
}); });
});
});
});
} }
...@@ -13,8 +13,8 @@ before(function() { ...@@ -13,8 +13,8 @@ before(function() {
}); });
}); });
beforeEach(function() { beforeEach(async function() {
return Support.clearDatabase(this.sequelize); await Support.clearDatabase(this.sequelize);
}); });
afterEach(function() { afterEach(function() {
......
...@@ -18,7 +18,7 @@ if (dialect !== 'sqlite') { ...@@ -18,7 +18,7 @@ if (dialect !== 'sqlite') {
}); });
}); });
it('returns the same value for current timestamp', function() { it('returns the same value for current timestamp', async function() {
let now = 'now()'; let now = 'now()';
const startQueryTime = Date.now(); const startQueryTime = Date.now();
...@@ -27,49 +27,46 @@ if (dialect !== 'sqlite') { ...@@ -27,49 +27,46 @@ if (dialect !== 'sqlite') {
} }
const query = `SELECT ${now} as now`; const query = `SELECT ${now} as now`;
return Promise.all([
const [now1, now2] = await Promise.all([
this.sequelize.query(query, { type: this.sequelize.QueryTypes.SELECT }), this.sequelize.query(query, { type: this.sequelize.QueryTypes.SELECT }),
this.sequelizeWithTimezone.query(query, { type: this.sequelize.QueryTypes.SELECT }) this.sequelizeWithTimezone.query(query, { type: this.sequelize.QueryTypes.SELECT })
]).then(([now1, now2]) => { ]);
const elapsedQueryTime = Date.now() - startQueryTime + 1001; const elapsedQueryTime = Date.now() - startQueryTime + 1001;
expect(now1[0].now.getTime()).to.be.closeTo(now2[0].now.getTime(), elapsedQueryTime); expect(now1[0].now.getTime()).to.be.closeTo(now2[0].now.getTime(), elapsedQueryTime);
}); });
});
if (dialect === 'mysql' || dialect === 'mariadb') { if (dialect === 'mysql' || dialect === 'mariadb') {
it('handles existing timestamps', function() { it('handles existing timestamps', async function() {
const NormalUser = this.sequelize.define('user', {}), const NormalUser = this.sequelize.define('user', {}),
TimezonedUser = this.sequelizeWithTimezone.define('user', {}); TimezonedUser = this.sequelizeWithTimezone.define('user', {});
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return NormalUser.create({}); const normalUser = await NormalUser.create({});
}).then(normalUser => {
this.normalUser = normalUser; this.normalUser = normalUser;
return TimezonedUser.findByPk(normalUser.id); const timezonedUser = await TimezonedUser.findByPk(normalUser.id);
}).then(timezonedUser => {
// Expect 7 hours difference, in milliseconds. // Expect 7 hours difference, in milliseconds.
// This difference is expected since two instances, configured for each their timezone is trying to read the same timestamp // This difference is expected since two instances, configured for each their timezone is trying to read the same timestamp
// this test does not apply to PG, since it stores the timezone along with the timestamp. // this test does not apply to PG, since it stores the timezone along with the timestamp.
expect(this.normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 7 * 1000, 1000); expect(this.normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 7 * 1000, 1000);
}); });
});
it('handles named timezones', function() { it('handles named timezones', async function() {
const NormalUser = this.sequelize.define('user', {}), const NormalUser = this.sequelize.define('user', {}),
TimezonedUser = this.sequelizeWithNamedTimezone.define('user', {}); TimezonedUser = this.sequelizeWithNamedTimezone.define('user', {});
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return TimezonedUser.create({}); const timezonedUser0 = await TimezonedUser.create({});
}).then(timezonedUser => {
return Promise.all([ const [normalUser, timezonedUser] = await Promise.all([
NormalUser.findByPk(timezonedUser.id), NormalUser.findByPk(timezonedUser0.id),
TimezonedUser.findByPk(timezonedUser.id) TimezonedUser.findByPk(timezonedUser0.id)
]); ]);
}).then(([normalUser, timezonedUser]) => {
// Expect 5 hours difference, in milliseconds, +/- 1 hour for DST // Expect 5 hours difference, in milliseconds, +/- 1 hour for DST
expect(normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 4 * 1000 * -1, 60 * 60 * 1000); expect(normalUser.createdAt.getTime() - timezonedUser.createdAt.getTime()).to.be.closeTo(60 * 60 * 4 * 1000 * -1, 60 * 60 * 1000);
}); });
});
} }
}); });
} }
...@@ -55,56 +55,60 @@ if (current.dialect.supports.transactions) { ...@@ -55,56 +55,60 @@ if (current.dialect.supports.transactions) {
}); });
describe('autoCallback', () => { describe('autoCallback', () => {
it('supports automatically committing', function() { it('supports automatically committing', async function() {
return this.sequelize.transaction(() => { await this.sequelize.transaction(async () => {});
return Promise.resolve();
});
}); });
it('supports automatically rolling back with a thrown error', function() { it('supports automatically rolling back with a thrown error', async function() {
let t; let t;
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(transaction => {
t = transaction; t = transaction;
throw new Error('Yolo'); throw new Error('Yolo');
})).to.eventually.be.rejected.then(() => { })).to.eventually.be.rejected;
expect(t.finished).to.be.equal('rollback'); expect(t.finished).to.be.equal('rollback');
}); });
});
it('supports automatically rolling back with a rejection', function() { it('supports automatically rolling back with a rejection', async function() {
let t; let t;
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(async transaction => {
t = transaction; t = transaction;
return Promise.reject(new Error('Swag')); throw new Error('Swag');
})).to.eventually.be.rejected.then(() => { })).to.eventually.be.rejected;
expect(t.finished).to.be.equal('rollback'); expect(t.finished).to.be.equal('rollback');
}); });
});
it('supports running hooks when a transaction is committed', function() { it('supports running hooks when a transaction is committed', async function() {
const hook = sinon.spy(); const hook = sinon.spy();
let transaction; let transaction;
return expect(this.sequelize.transaction(t => {
await expect((async () => {
await this.sequelize.transaction(t => {
transaction = t; transaction = t;
transaction.afterCommit(hook); transaction.afterCommit(hook);
return this.sequelize.query('SELECT 1+1', { transaction, type: QueryTypes.SELECT }); return this.sequelize.query('SELECT 1+1', { transaction, type: QueryTypes.SELECT });
}).then(() => { });
expect(hook).to.have.been.calledOnce; expect(hook).to.have.been.calledOnce;
expect(hook).to.have.been.calledWith(transaction); expect(hook).to.have.been.calledWith(transaction);
}) })()
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
}); });
it('does not run hooks when a transaction is rolled back', function() { it('does not run hooks when a transaction is rolled back', async function() {
const hook = sinon.spy(); const hook = sinon.spy();
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(async transaction => {
transaction.afterCommit(hook); transaction.afterCommit(hook);
return Promise.reject(new Error('Rollback')); throw new Error('Rollback');
}) })
).to.eventually.be.rejected.then(() => { ).to.eventually.be.rejected;
expect(hook).to.not.have.been.called; expect(hook).to.not.have.been.called;
}); });
});
//Promise rejection test is specific to postgres //Promise rejection test is specific to postgres
if (dialect === 'postgres') { if (dialect === 'postgres') {
...@@ -152,7 +156,8 @@ if (current.dialect.supports.transactions) { ...@@ -152,7 +156,8 @@ if (current.dialect.supports.transactions) {
}); });
it('does not allow queries immediately after commit call', async function() { it('does not allow queries immediately after commit call', async function() {
await expect(this.sequelize.transaction().then(async t => { await expect((async () => {
const t = await this.sequelize.transaction();
await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true }); await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
await Promise.all([ await Promise.all([
expect(t.commit()).to.eventually.be.fulfilled, expect(t.commit()).to.eventually.be.fulfilled,
...@@ -161,28 +166,27 @@ if (current.dialect.supports.transactions) { ...@@ -161,28 +166,27 @@ if (current.dialect.supports.transactions) {
/commit has been called on this transaction\([^)]+\), you can no longer use it\. \(The rejected query is attached as the 'sql' property of this error\)/ /commit has been called on this transaction\([^)]+\), you can no longer use it\. \(The rejected query is attached as the 'sql' property of this error\)/
).and.have.deep.property('sql').that.equal('SELECT 1+1') ).and.have.deep.property('sql').that.equal('SELECT 1+1')
]); ]);
})).to.be.eventually.fulfilled; })()).to.be.eventually.fulfilled;
}); });
it('does not allow queries after rollback', function() { it('does not allow queries after rollback', async function() {
return expect( await expect(
this.sequelize.transaction().then(t => { (async () => {
return this.sequelize.query('SELECT 1+1', { transaction: t, raw: true }).then(() => { const t = await this.sequelize.transaction();
return t.rollback(); await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
}).then(() => { await t.rollback();
return this.sequelize.query('SELECT 1+1', { transaction: t, raw: true }); return await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
}); })()
})
).to.eventually.be.rejected; ).to.eventually.be.rejected;
}); });
it('should not rollback if connection was not acquired', function() { it('should not rollback if connection was not acquired', async function() {
this.sinon.stub(this.sequelize.connectionManager, '_connect') this.sinon.stub(this.sequelize.connectionManager, '_connect')
.returns(new Promise(() => {})); .returns(new Promise(() => {}));
const transaction = new Transaction(this.sequelize); const transaction = new Transaction(this.sequelize);
return expect(transaction.rollback()) await expect(transaction.rollback())
.to.eventually.be.rejectedWith('Transaction cannot be rolled back because it never started'); .to.eventually.be.rejectedWith('Transaction cannot be rolled back because it never started');
}); });
...@@ -200,174 +204,184 @@ if (current.dialect.supports.transactions) { ...@@ -200,174 +204,184 @@ if (current.dialect.supports.transactions) {
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
}); });
it('does not allow commits after commit', function() { it('does not allow commits after commit', async function() {
return expect( await expect(
this.sequelize.transaction().then(t => { (async () => {
return t.commit().then(() => { const t = await this.sequelize.transaction();
return t.commit(); await t.commit();
}); return await t.commit();
}) })()
).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: commit'); ).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: commit');
}); });
it('should run hooks if a non-auto callback transaction is committed', function() { it('should run hooks if a non-auto callback transaction is committed', async function() {
const hook = sinon.spy(); const hook = sinon.spy();
let transaction; let transaction;
return expect(
this.sequelize.transaction().then(t => { await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t; transaction = t;
transaction.afterCommit(hook); transaction.afterCommit(hook);
return t.commit().then(() => { await t.commit();
expect(hook).to.have.been.calledOnce; expect(hook).to.have.been.calledOnce;
expect(hook).to.have.been.calledWith(t); expect(hook).to.have.been.calledWith(t);
}); } catch (err) {
}).catch(err => {
// Cleanup this transaction so other tests don't // Cleanup this transaction so other tests don't
// fail due to an open transaction // fail due to an open transaction
if (!transaction.finished) { if (!transaction.finished) {
return transaction.rollback().then(() => { await transaction.rollback();
throw err; throw err;
});
} }
throw err; throw err;
}) }
})()
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
}); });
it('should not run hooks if a non-auto callback transaction is rolled back', function() { it('should not run hooks if a non-auto callback transaction is rolled back', async function() {
const hook = sinon.spy(); const hook = sinon.spy();
return expect(
this.sequelize.transaction().then(t => { await expect(
(async () => {
const t = await this.sequelize.transaction();
t.afterCommit(hook); t.afterCommit(hook);
return t.rollback().then(() => { await t.rollback();
expect(hook).to.not.have.been.called; expect(hook).to.not.have.been.called;
}); })()
})
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
}); });
it('should throw an error if null is passed to afterCommit', function() { it('should throw an error if null is passed to afterCommit', async function() {
const hook = null; const hook = null;
let transaction; let transaction;
return expect(
this.sequelize.transaction().then(t => { await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t; transaction = t;
transaction.afterCommit(hook); transaction.afterCommit(hook);
return t.commit(); return await t.commit();
}).catch(err => { } catch (err) {
// Cleanup this transaction so other tests don't // Cleanup this transaction so other tests don't
// fail due to an open transaction // fail due to an open transaction
if (!transaction.finished) { if (!transaction.finished) {
return transaction.rollback().then(() => { await transaction.rollback();
throw err; throw err;
});
} }
throw err; throw err;
}) }
})()
).to.eventually.be.rejectedWith('"fn" must be a function'); ).to.eventually.be.rejectedWith('"fn" must be a function');
}); });
it('should throw an error if undefined is passed to afterCommit', function() { it('should throw an error if undefined is passed to afterCommit', async function() {
const hook = undefined; const hook = undefined;
let transaction; let transaction;
return expect(
this.sequelize.transaction().then(t => { await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t; transaction = t;
transaction.afterCommit(hook); transaction.afterCommit(hook);
return t.commit(); return await t.commit();
}).catch(err => { } catch (err) {
// Cleanup this transaction so other tests don't // Cleanup this transaction so other tests don't
// fail due to an open transaction // fail due to an open transaction
if (!transaction.finished) { if (!transaction.finished) {
return transaction.rollback().then(() => { await transaction.rollback();
throw err; throw err;
});
} }
throw err; throw err;
}) }
})()
).to.eventually.be.rejectedWith('"fn" must be a function'); ).to.eventually.be.rejectedWith('"fn" must be a function');
}); });
it('should throw an error if an object is passed to afterCommit', function() { it('should throw an error if an object is passed to afterCommit', async function() {
const hook = {}; const hook = {};
let transaction; let transaction;
return expect(
this.sequelize.transaction().then(t => { await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t; transaction = t;
transaction.afterCommit(hook); transaction.afterCommit(hook);
return t.commit(); return await t.commit();
}).catch(err => { } catch (err) {
// Cleanup this transaction so other tests don't // Cleanup this transaction so other tests don't
// fail due to an open transaction // fail due to an open transaction
if (!transaction.finished) { if (!transaction.finished) {
return transaction.rollback().then(() => { await transaction.rollback();
throw err; throw err;
});
} }
throw err; throw err;
}) }
})()
).to.eventually.be.rejectedWith('"fn" must be a function'); ).to.eventually.be.rejectedWith('"fn" must be a function');
}); });
it('does not allow commits after rollback', function() { it('does not allow commits after rollback', async function() {
return expect(this.sequelize.transaction().then(t => { await expect((async () => {
return t.rollback().then(() => { const t = await this.sequelize.transaction();
return t.commit(); await t.rollback();
}); return await t.commit();
})).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: rollback'); })()).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: rollback');
}); });
it('does not allow rollbacks after commit', function() { it('does not allow rollbacks after commit', async function() {
return expect(this.sequelize.transaction().then(t => { await expect((async () => {
return t.commit().then(() => { const t = await this.sequelize.transaction();
return t.rollback(); await t.commit();
}); return await t.rollback();
})).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: commit'); })()).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: commit');
}); });
it('does not allow rollbacks after rollback', function() { it('does not allow rollbacks after rollback', async function() {
return expect(this.sequelize.transaction().then(t => { await expect((async () => {
return t.rollback().then(() => { const t = await this.sequelize.transaction();
return t.rollback(); await t.rollback();
}); return await t.rollback();
})).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: rollback'); })()).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: rollback');
}); });
it('works even if a transaction: null option is passed', function() { it('works even if a transaction: null option is passed', async function() {
this.sinon.spy(this.sequelize, 'query'); this.sinon.spy(this.sequelize, 'query');
return this.sequelize.transaction({ const t = await this.sequelize.transaction({
transaction: null transaction: null
}).then(t => { });
return t.commit().then(() => {
await t.commit();
expect(this.sequelize.query.callCount).to.be.greaterThan(0); expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) { for (let i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t); expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
} }
}); });
});
});
it('works even if a transaction: undefined option is passed', function() { it('works even if a transaction: undefined option is passed', async function() {
this.sinon.spy(this.sequelize, 'query'); this.sinon.spy(this.sequelize, 'query');
return this.sequelize.transaction({ const t = await this.sequelize.transaction({
transaction: undefined transaction: undefined
}).then(t => { });
return t.commit().then(() => {
await t.commit();
expect(this.sequelize.query.callCount).to.be.greaterThan(0); expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) { for (let i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t); expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
} }
}); });
});
});
if (dialect === 'mysql' || dialect === 'mariadb') { if (dialect === 'mysql' || dialect === 'mariadb') {
describe('deadlock handling', () => { describe('deadlock handling', () => {
it('should treat deadlocked transaction as rollback', function() { it('should treat deadlocked transaction as rollback', async function() {
const Task = this.sequelize.define('task', { const Task = this.sequelize.define('task', {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -382,8 +396,10 @@ if (current.dialect.supports.transactions) { ...@@ -382,8 +396,10 @@ if (current.dialect.supports.transactions) {
// execute a query, we expect the newly-created rows to be destroyed when we forcibly rollback by // execute a query, we expect the newly-created rows to be destroyed when we forcibly rollback by
// throwing an error. // throwing an error.
// tl;dr; This test is designed to ensure that this function never inserts and commits a new row. // tl;dr; This test is designed to ensure that this function never inserts and commits a new row.
const update = (from, to) => this.sequelize.transaction(transaction => { const update = async (from, to) => this.sequelize.transaction(async transaction => {
return Task.findAll({ try {
try {
await Task.findAll({
where: { where: {
id: { id: {
[Sequelize.Op.eq]: from [Sequelize.Op.eq]: from
...@@ -391,10 +407,11 @@ if (current.dialect.supports.transactions) { ...@@ -391,10 +407,11 @@ if (current.dialect.supports.transactions) {
}, },
lock: 'UPDATE', lock: 'UPDATE',
transaction transaction
}) });
.then(() => delay(10))
.then(() => { await delay(10);
return Task.update({ id: to }, {
await Task.update({ id: to }, {
where: { where: {
id: { id: {
[Sequelize.Op.ne]: to [Sequelize.Op.ne]: to
...@@ -403,80 +420,73 @@ if (current.dialect.supports.transactions) { ...@@ -403,80 +420,73 @@ if (current.dialect.supports.transactions) {
lock: transaction.LOCK.UPDATE, lock: transaction.LOCK.UPDATE,
transaction transaction
}); });
}) } catch (e) {
.catch(e => { console.log(e.message); }) console.log(e.message);
.then(() => Task.create({ id: 2 }, { transaction })) }
.catch(e => { console.log(e.message); })
.then(() => { throw new Error('Rollback!'); }); await Task.create({ id: 2 }, { transaction });
} catch (e) {
console.log(e.message);
}
throw new Error('Rollback!');
}).catch(() => {}); }).catch(() => {});
return this.sequelize.sync({ force: true }) await this.sequelize.sync({ force: true });
.then(() => Task.create({ id: 0 })) await Task.create({ id: 0 });
.then(() => Task.create({ id: 1 })) await Task.create({ id: 1 });
.then(() => Promise.all([
await Promise.all([
update(1, 0), update(1, 0),
update(0, 1) update(0, 1)
])) ]);
.then(() => {
return Task.count().then(count => { const count = await Task.count();
// If we were actually inside a transaction when we called `Task.create({ id: 2 })`, no new rows should be added. // If we were actually inside a transaction when we called `Task.create({ id: 2 })`, no new rows should be added.
expect(count).to.equal(2, 'transactions were fully rolled-back, and no new rows were added'); expect(count).to.equal(2, 'transactions were fully rolled-back, and no new rows were added');
}); });
}); });
});
});
} }
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('provides persistent transactions', () => { it('provides persistent transactions', async () => {
const sequelize = new Support.Sequelize('database', 'username', 'password', { dialect: 'sqlite' }), const sequelize = new Support.Sequelize('database', 'username', 'password', { dialect: 'sqlite' }),
User = sequelize.define('user', { User = sequelize.define('user', {
username: Support.Sequelize.STRING, username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN awesome: Support.Sequelize.BOOLEAN
}); });
let persistentTransaction;
return sequelize.transaction().then(t => { const t1 = await sequelize.transaction();
return sequelize.sync({ transaction: t }).then(( ) => { await sequelize.sync({ transaction: t1 });
return t; const t0 = t1;
}); await User.create({}, { transaction: t0 });
}).then(t => { await t0.commit();
return User.create({}, { transaction: t }).then(( ) => { const persistentTransaction = await sequelize.transaction();
return t.commit(); const users = await User.findAll({ transaction: persistentTransaction });
});
}).then(() => {
return sequelize.transaction().then(t => {
persistentTransaction = t;
});
}).then(() => {
return User.findAll({ transaction: persistentTransaction }).then(users => {
expect(users.length).to.equal(1); expect(users.length).to.equal(1);
return persistentTransaction.commit();
}); await persistentTransaction.commit();
});
}); });
} }
if (current.dialect.supports.transactionOptions.type) { if (current.dialect.supports.transactionOptions.type) {
describe('transaction types', () => { describe('transaction types', () => {
it('should support default transaction type DEFERRED', function() { it('should support default transaction type DEFERRED', async function() {
return this.sequelize.transaction({ const t = await this.sequelize.transaction({
}).then(t => {
return t.rollback().then(() => {
expect(t.options.type).to.equal('DEFERRED');
});
}); });
await t.rollback();
expect(t.options.type).to.equal('DEFERRED');
}); });
Object.keys(Transaction.TYPES).forEach(key => { Object.keys(Transaction.TYPES).forEach(key => {
it(`should allow specification of ${key} type`, function() { it(`should allow specification of ${key} type`, async function() {
return this.sequelize.transaction({ const t = await this.sequelize.transaction({
type: key type: key
}).then(t => {
return t.rollback().then(() => {
expect(t.options.type).to.equal(Transaction.TYPES[key]);
});
}); });
await t.rollback();
expect(t.options.type).to.equal(Transaction.TYPES[key]);
}); });
}); });
...@@ -485,61 +495,50 @@ if (current.dialect.supports.transactions) { ...@@ -485,61 +495,50 @@ if (current.dialect.supports.transactions) {
} }
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('automatically retries on SQLITE_BUSY failure', function() { it('automatically retries on SQLITE_BUSY failure', async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { username: Support.Sequelize.STRING }); const User = sequelize.define('User', { username: Support.Sequelize.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
const newTransactionFunc = function() { const newTransactionFunc = async function() {
return sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE }).then(t => { const t = await sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE });
return User.create({}, { transaction: t }).then(( ) => { await User.create({}, { transaction: t });
return t.commit(); return t.commit();
});
});
}; };
return Promise.all([newTransactionFunc(), newTransactionFunc()]).then(() => { await Promise.all([newTransactionFunc(), newTransactionFunc()]);
return User.findAll().then(users => { const users = await User.findAll();
expect(users.length).to.equal(2); expect(users.length).to.equal(2);
}); });
});
});
});
});
it('fails with SQLITE_BUSY when retry.match is changed', function() { it('fails with SQLITE_BUSY when retry.match is changed', async function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => { const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { id: { type: Support.Sequelize.INTEGER, primaryKey: true }, username: Support.Sequelize.STRING }); const User = sequelize.define('User', { id: { type: Support.Sequelize.INTEGER, primaryKey: true }, username: Support.Sequelize.STRING });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
const newTransactionFunc = function() { const newTransactionFunc = async function() {
return sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE, retry: { match: ['NO_MATCH'] } }).then(t => { const t = await sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE, retry: { match: ['NO_MATCH'] } });
// introduce delay to force the busy state race condition to fail // introduce delay to force the busy state race condition to fail
return delay(1000).then(() => { await delay(1000);
return User.create({ id: null, username: `test ${t.id}` }, { transaction: t }).then(() => { await User.create({ id: null, username: `test ${t.id}` }, { transaction: t });
return t.commit(); return t.commit();
});
});
});
}; };
return expect(Promise.all([newTransactionFunc(), newTransactionFunc()])).to.be.rejectedWith('SQLITE_BUSY: database is locked'); await expect(Promise.all([newTransactionFunc(), newTransactionFunc()])).to.be.rejectedWith('SQLITE_BUSY: database is locked');
});
});
}); });
} }
describe('isolation levels', () => { describe('isolation levels', () => {
it('should read the most recent committed rows when using the READ COMMITTED isolation level', function() { it('should read the most recent committed rows when using the READ COMMITTED isolation level', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING username: Support.Sequelize.STRING
}); });
return expect( await expect(
this.sequelize.sync({ force: true }).then(() => { this.sequelize.sync({ force: true }).then(() => {
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }, transaction => { return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async transaction => {
return User.findAll({ transaction }) const users0 = await User.findAll({ transaction });
.then(users => expect( users ).to.have.lengthOf(0)) await expect( users0 ).to.have.lengthOf(0);
.then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction await User.create({ username: 'jan' }); // Create a User outside of the transaction
.then(() => User.findAll({ transaction })) const users = await User.findAll({ transaction });
.then(users => expect( users ).to.have.lengthOf(1)); // We SHOULD see the created user inside the transaction return expect( users ).to.have.lengthOf(1); // We SHOULD see the created user inside the transaction
}); });
}) })
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
...@@ -547,19 +546,19 @@ if (current.dialect.supports.transactions) { ...@@ -547,19 +546,19 @@ if (current.dialect.supports.transactions) {
// mssql is excluded because it implements REPREATABLE READ using locks rather than a snapshot, and will see the new row // mssql is excluded because it implements REPREATABLE READ using locks rather than a snapshot, and will see the new row
if (!['sqlite', 'mssql'].includes(dialect)) { if (!['sqlite', 'mssql'].includes(dialect)) {
it('should not read newly committed rows when using the REPEATABLE READ isolation level', function() { it('should not read newly committed rows when using the REPEATABLE READ isolation level', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING username: Support.Sequelize.STRING
}); });
return expect( await expect(
this.sequelize.sync({ force: true }).then(() => { this.sequelize.sync({ force: true }).then(() => {
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ }, transaction => { return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ }, async transaction => {
return User.findAll({ transaction }) const users0 = await User.findAll({ transaction });
.then(users => expect( users ).to.have.lengthOf(0)) await expect( users0 ).to.have.lengthOf(0);
.then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction await User.create({ username: 'jan' }); // Create a User outside of the transaction
.then(() => User.findAll({ transaction })) const users = await User.findAll({ transaction });
.then(users => expect( users ).to.have.lengthOf(0)); // We SHOULD NOT see the created user inside the transaction return expect( users ).to.have.lengthOf(0); // We SHOULD NOT see the created user inside the transaction
}); });
}) })
).to.eventually.be.fulfilled; ).to.eventually.be.fulfilled;
...@@ -568,27 +567,25 @@ if (current.dialect.supports.transactions) { ...@@ -568,27 +567,25 @@ if (current.dialect.supports.transactions) {
// PostgreSQL is excluded because it detects Serialization Failure on commit instead of acquiring locks on the read rows // PostgreSQL is excluded because it detects Serialization Failure on commit instead of acquiring locks on the read rows
if (!['sqlite', 'postgres', 'postgres-native'].includes(dialect)) { if (!['sqlite', 'postgres', 'postgres-native'].includes(dialect)) {
it('should block updates after reading a row using SERIALIZABLE', function() { it('should block updates after reading a row using SERIALIZABLE', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING username: Support.Sequelize.STRING
}), }),
transactionSpy = sinon.spy(); transactionSpy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({ username: 'jan' }); await User.create({ username: 'jan' });
}).then(() => { const transaction = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE });
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE }).then(transaction => { await User.findAll( { transaction } );
return User.findAll( { transaction } )
.then(() => Promise.all([// Update should not succeed before transaction has committed await Promise.all([// Update should not succeed before transaction has committed
User.update({ username: 'joe' }, { User.update({ username: 'joe' }, {
where: { where: {
username: 'jan' username: 'jan'
} }
}).then(() => expect(transactionSpy).to.have.been.called ), delay(2000) }).then(() => expect(transactionSpy).to.have.been.called ), delay(2000)
.then(() => transaction.commit()) .then(() => transaction.commit())
.then(transactionSpy)])); .then(transactionSpy)]);
});
});
}); });
} }
...@@ -597,7 +594,7 @@ if (current.dialect.supports.transactions) { ...@@ -597,7 +594,7 @@ if (current.dialect.supports.transactions) {
if (current.dialect.supports.lock) { if (current.dialect.supports.lock) {
describe('row locking', () => { describe('row locking', () => {
it('supports for update', function() { it('supports for update', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING, username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN awesome: Support.Sequelize.BOOLEAN
...@@ -605,56 +602,57 @@ if (current.dialect.supports.transactions) { ...@@ -605,56 +602,57 @@ if (current.dialect.supports.transactions) {
t1Spy = sinon.spy(), t1Spy = sinon.spy(),
t2Spy = sinon.spy(); t2Spy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({ username: 'jan' }); await User.create({ username: 'jan' });
}).then(() => { const t1 = await this.sequelize.transaction();
return this.sequelize.transaction().then(t1 => {
return User.findOne({ const t1Jan = await User.findOne({
where: { where: {
username: 'jan' username: 'jan'
}, },
lock: t1.LOCK.UPDATE, lock: t1.LOCK.UPDATE,
transaction: t1 transaction: t1
}).then(t1Jan => { });
return this.sequelize.transaction({
const t2 = await this.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
}).then(t2 => { });
return Promise.all([User.findOne({
await Promise.all([(async () => {
await User.findOne({
where: { where: {
username: 'jan' username: 'jan'
}, },
lock: t2.LOCK.UPDATE, lock: t2.LOCK.UPDATE,
transaction: t2 transaction: t2
}).then(() => { });
t2Spy(); t2Spy();
return t2.commit().then(() => { await t2.commit();
expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has committed expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has committed
}); })(), (async () => {
}), t1Jan.update({ await t1Jan.update({
awesome: true awesome: true
}, { }, {
transaction: t1 transaction: t1
}).then(() => {
t1Spy();
return delay(2000).then(() => {
return t1.commit();
});
})]);
});
});
});
}); });
t1Spy();
await delay(2000);
return await t1.commit();
})()]);
}); });
if (current.dialect.supports.skipLocked) { if (current.dialect.supports.skipLocked) {
it('supports for update with skip locked', function() { it('supports for update with skip locked', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING, username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN awesome: Support.Sequelize.BOOLEAN
}); });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
await Promise.all([
User.create( User.create(
{ username: 'jan' } { username: 'jan' }
), ),
...@@ -662,50 +660,51 @@ if (current.dialect.supports.transactions) { ...@@ -662,50 +660,51 @@ if (current.dialect.supports.transactions) {
{ username: 'joe' } { username: 'joe' }
) )
]); ]);
}).then(() => {
return this.sequelize.transaction().then(t1 => { const t1 = await this.sequelize.transaction();
return User.findAll({
const results = await User.findAll({
limit: 1, limit: 1,
lock: true, lock: true,
transaction: t1 transaction: t1
}).then(results => { });
const firstUserId = results[0].id; const firstUserId = results[0].id;
return this.sequelize.transaction().then(t2 => { const t2 = await this.sequelize.transaction();
return User.findAll({
const secondResults = await User.findAll({
limit: 1, limit: 1,
lock: true, lock: true,
skipLocked: true, skipLocked: true,
transaction: t2 transaction: t2
}).then(secondResults => { });
expect(secondResults[0].id).to.not.equal(firstUserId); expect(secondResults[0].id).to.not.equal(firstUserId);
return Promise.all([
await Promise.all([
t1.commit(), t1.commit(),
t2.commit() t2.commit()
]); ]);
}); });
});
});
});
});
});
} }
it('fail locking with outer joins', function() { it('fail locking with outer joins', async function() {
const User = this.sequelize.define('User', { username: Support.Sequelize.STRING }), const User = this.sequelize.define('User', { username: Support.Sequelize.STRING }),
Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN }); Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN });
User.belongsToMany(Task, { through: 'UserTasks' }); User.belongsToMany(Task, { through: 'UserTasks' });
Task.belongsToMany(User, { through: 'UserTasks' }); Task.belongsToMany(User, { through: 'UserTasks' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const [john, task1] = await Promise.all([
User.create({ username: 'John' }), User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false }) Task.create({ title: 'Get rich', active: false })
]).then(([john, task1]) => { ]);
return john.setTasks([task1]);
}) await john.setTasks([task1]);
.then(() => {
return this.sequelize.transaction(t1 => { await this.sequelize.transaction(t1 => {
if (current.dialect.supports.lockOuterJoinFailure) { if (current.dialect.supports.lockOuterJoinFailure) {
...@@ -729,28 +728,27 @@ if (current.dialect.supports.transactions) { ...@@ -729,28 +728,27 @@ if (current.dialect.supports.transactions) {
}); });
}); });
}); });
});
});
if (current.dialect.supports.lockOf) { if (current.dialect.supports.lockOf) {
it('supports for update of table', function() { it('supports for update of table', async function() {
const User = this.sequelize.define('User', { username: Support.Sequelize.STRING }, { tableName: 'Person' }), const User = this.sequelize.define('User', { username: Support.Sequelize.STRING }, { tableName: 'Person' }),
Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN }); Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN });
User.belongsToMany(Task, { through: 'UserTasks' }); User.belongsToMany(Task, { through: 'UserTasks' });
Task.belongsToMany(User, { through: 'UserTasks' }); Task.belongsToMany(User, { through: 'UserTasks' });
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return Promise.all([
const [john, task1] = await Promise.all([
User.create({ username: 'John' }), User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false }), Task.create({ title: 'Get rich', active: false }),
Task.create({ title: 'Die trying', active: false }) Task.create({ title: 'Die trying', active: false })
]).then(([john, task1]) => { ]);
return john.setTasks([task1]);
}) await john.setTasks([task1]);
.then(() => {
return this.sequelize.transaction(t1 => { await this.sequelize.transaction(async t1 => {
return User.findOne({ const t1John = await User.findOne({
where: { where: {
username: 'John' username: 'John'
}, },
...@@ -760,9 +758,10 @@ if (current.dialect.supports.transactions) { ...@@ -760,9 +758,10 @@ if (current.dialect.supports.transactions) {
of: User of: User
}, },
transaction: t1 transaction: t1
}).then(t1John => { });
// should not be blocked by the lock of the other transaction // should not be blocked by the lock of the other transaction
return this.sequelize.transaction(t2 => { await this.sequelize.transaction(t2 => {
return Task.update({ return Task.update({
active: true active: true
}, { }, {
...@@ -771,20 +770,17 @@ if (current.dialect.supports.transactions) { ...@@ -771,20 +770,17 @@ if (current.dialect.supports.transactions) {
}, },
transaction: t2 transaction: t2
}); });
}).then(() => { });
return t1John.save({ return t1John.save({
transaction: t1 transaction: t1
}); });
}); });
}); });
});
});
});
});
} }
if (current.dialect.supports.lockKey) { if (current.dialect.supports.lockKey) {
it('supports for key share', function() { it('supports for key share', async function() {
const User = this.sequelize.define('user', { const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING, username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN awesome: Support.Sequelize.BOOLEAN
...@@ -792,42 +788,43 @@ if (current.dialect.supports.transactions) { ...@@ -792,42 +788,43 @@ if (current.dialect.supports.transactions) {
t1Spy = sinon.spy(), t1Spy = sinon.spy(),
t2Spy = sinon.spy(); t2Spy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return User.create({ username: 'jan' }); await User.create({ username: 'jan' });
}).then(() => { const t1 = await this.sequelize.transaction();
return this.sequelize.transaction().then(t1 => {
return User.findOne({ const t1Jan = await User.findOne({
where: { where: {
username: 'jan' username: 'jan'
}, },
lock: t1.LOCK.NO_KEY_UPDATE, lock: t1.LOCK.NO_KEY_UPDATE,
transaction: t1 transaction: t1
}).then(t1Jan => { });
return this.sequelize.transaction().then(t2 => {
return Promise.all([User.findOne({ const t2 = await this.sequelize.transaction();
await Promise.all([(async () => {
await User.findOne({
where: { where: {
username: 'jan' username: 'jan'
}, },
lock: t2.LOCK.KEY_SHARE, lock: t2.LOCK.KEY_SHARE,
transaction: t2 transaction: t2
}).then(() => { });
t2Spy(); t2Spy();
return t2.commit(); return await t2.commit();
}), t1Jan.update({ })(), (async () => {
await t1Jan.update({
awesome: true awesome: true
}, { }, {
transaction: t1 transaction: t1
}).then(() => { });
return delay(2000).then(() => {
await delay(2000);
t1Spy(); t1Spy();
expect(t1Spy).to.have.been.calledAfter(t2Spy); expect(t1Spy).to.have.been.calledAfter(t2Spy);
return t1.commit(); return await t1.commit();
}); })()]);
})]);
});
});
});
});
}); });
} }
...@@ -855,21 +852,24 @@ if (current.dialect.supports.transactions) { ...@@ -855,21 +852,24 @@ if (current.dialect.supports.transactions) {
}); });
await Promise.all([ await Promise.all([
User.findByPk(user.id, { (async () => {
const t2Jan = await User.findByPk(user.id, {
transaction: t2 transaction: t2
}).then(async t2Jan => { });
t2FindSpy(); t2FindSpy();
await t2Jan.update({ awesome: false }, { transaction: t2 }); await t2Jan.update({ awesome: false }, { transaction: t2 });
t2UpdateSpy(); t2UpdateSpy();
await t2.commit(); await t2.commit();
}), })(),
t1Jan.update({ awesome: true }, { transaction: t1 }).then(async () => { (async () => {
await t1Jan.update({ awesome: true }, { transaction: t1 });
await delay(2000); await delay(2000);
t1CommitSpy(); t1CommitSpy();
await t1.commit(); await t1.commit();
}) })()
]); ]);
// (t2) find call should have returned before (t1) commit // (t2) find call should have returned before (t1) commit
......
...@@ -22,7 +22,7 @@ if (current.dialect.supports.tmpTableTrigger) { ...@@ -22,7 +22,7 @@ if (current.dialect.supports.tmpTableTrigger) {
'select * from deleted\n' + 'select * from deleted\n' +
'end\n'; 'end\n';
beforeEach(function() { beforeEach(async function() {
User = this.sequelize.define('user', { User = this.sequelize.define('user', {
username: { username: {
type: Sequelize.STRING, type: Sequelize.STRING,
...@@ -32,56 +32,52 @@ if (current.dialect.supports.tmpTableTrigger) { ...@@ -32,56 +32,52 @@ if (current.dialect.supports.tmpTableTrigger) {
hasTrigger: true hasTrigger: true
}); });
return User.sync({ force: true }).then(() => { await User.sync({ force: true });
return this.sequelize.query(triggerQuery, { type: this.sequelize.QueryTypes.RAW });
}); await this.sequelize.query(triggerQuery, { type: this.sequelize.QueryTypes.RAW });
}); });
it('should return output rows after insert', () => { it('should return output rows after insert', async () => {
return User.create({ await User.create({
username: 'triggertest' username: 'triggertest'
}).then(() => {
return expect(User.findOne({ username: 'triggertest' })).to.eventually.have.property('username').which.equals('triggertest');
}); });
await expect(User.findOne({ username: 'triggertest' })).to.eventually.have.property('username').which.equals('triggertest');
}); });
it('should return output rows after instance update', () => { it('should return output rows after instance update', async () => {
return User.create({ const user = await User.create({
username: 'triggertest' username: 'triggertest'
}).then(user => {
user.username = 'usernamechanged';
return user.save();
})
.then(() => {
return expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
}); });
user.username = 'usernamechanged';
await user.save();
await expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
}); });
it('should return output rows after Model update', () => { it('should return output rows after Model update', async () => {
return User.create({ const user = await User.create({
username: 'triggertest' username: 'triggertest'
}).then(user => { });
return User.update({
await User.update({
username: 'usernamechanged' username: 'usernamechanged'
}, { }, {
where: { where: {
id: user.get('id') id: user.get('id')
} }
}); });
})
.then(() => { await expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
return expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
});
}); });
it('should successfully delete with a trigger on the table', () => { it('should successfully delete with a trigger on the table', async () => {
return User.create({ const user = await User.create({
username: 'triggertest' username: 'triggertest'
}).then(user => {
return user.destroy();
}).then(() => {
return expect(User.findOne({ username: 'triggertest' })).to.eventually.be.null;
}); });
await user.destroy();
await expect(User.findOne({ username: 'triggertest' })).to.eventually.be.null;
}); });
}); });
}); });
......
...@@ -138,14 +138,15 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -138,14 +138,15 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
describe('Sequelize.fn', () => { describe('Sequelize.fn', () => {
let Airplane; let Airplane;
beforeEach(function() { beforeEach(async function() {
Airplane = this.sequelize.define('Airplane', { Airplane = this.sequelize.define('Airplane', {
wings: DataTypes.INTEGER, wings: DataTypes.INTEGER,
engines: DataTypes.INTEGER engines: DataTypes.INTEGER
}); });
return Airplane.sync({ force: true }).then(() => { await Airplane.sync({ force: true });
return Airplane.bulkCreate([
await Airplane.bulkCreate([
{ {
wings: 2, wings: 2,
engines: 0 engines: 0
...@@ -158,13 +159,12 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -158,13 +159,12 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
} }
]); ]);
}); });
});
if (Support.getTestDialect() !== 'mssql') { if (Support.getTestDialect() !== 'mssql') {
it('accepts condition object (with cast)', function() { it('accepts condition object (with cast)', async function() {
const type = Support.getTestDialect() === 'mysql' ? 'unsigned' : 'int'; const type = Support.getTestDialect() === 'mysql' ? 'unsigned' : 'int';
return Airplane.findAll({ const [airplane] = await Airplane.findAll({
attributes: [ attributes: [
[this.sequelize.fn('COUNT', '*'), 'count'], [this.sequelize.fn('COUNT', '*'), 'count'],
[Sequelize.fn('SUM', Sequelize.cast({ [Sequelize.fn('SUM', Sequelize.cast({
...@@ -179,18 +179,18 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -179,18 +179,18 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
} }
}, type)), 'count-engines-wings'] }, type)), 'count-engines-wings']
] ]
}).then(([airplane]) => { });
// TODO: `parseInt` should not be needed, see #10533 // TODO: `parseInt` should not be needed, see #10533
expect(parseInt(airplane.get('count'), 10)).to.equal(3); expect(parseInt(airplane.get('count'), 10)).to.equal(3);
expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1); expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1);
expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2); expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2);
}); });
});
} }
if (Support.getTestDialect() !== 'mssql' && Support.getTestDialect() !== 'postgres') { if (Support.getTestDialect() !== 'mssql' && Support.getTestDialect() !== 'postgres') {
it('accepts condition object (auto casting)', function() { it('accepts condition object (auto casting)', async function() {
return Airplane.findAll({ const [airplane] = await Airplane.findAll({
attributes: [ attributes: [
[this.sequelize.fn('COUNT', '*'), 'count'], [this.sequelize.fn('COUNT', '*'), 'count'],
[Sequelize.fn('SUM', { [Sequelize.fn('SUM', {
...@@ -205,13 +205,13 @@ describe(Support.getTestDialectTeaser('Utils'), () => { ...@@ -205,13 +205,13 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
} }
}), 'count-engines-wings'] }), 'count-engines-wings']
] ]
}).then(([airplane]) => { });
// TODO: `parseInt` should not be needed, see #10533 // TODO: `parseInt` should not be needed, see #10533
expect(airplane.get('count')).to.equal(3); expect(airplane.get('count')).to.equal(3);
expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1); expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1);
expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2); expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2);
}); });
});
} }
}); });
......
...@@ -8,22 +8,21 @@ const chai = require('chai'), ...@@ -8,22 +8,21 @@ const chai = require('chai'),
chai.should(); chai.should();
describe(Support.getTestDialectTeaser('Vectors'), () => { describe(Support.getTestDialectTeaser('Vectors'), () => {
it('should not allow insert backslash', function() { it('should not allow insert backslash', async function() {
const Student = this.sequelize.define('student', { const Student = this.sequelize.define('student', {
name: Sequelize.STRING name: Sequelize.STRING
}, { }, {
tableName: 'student' tableName: 'student'
}); });
return Student.sync({ force: true }).then(() => { await Student.sync({ force: true });
return Student.create({
const result0 = await Student.create({
name: 'Robert\\\'); DROP TABLE "students"; --' name: 'Robert\\\'); DROP TABLE "students"; --'
}).then(result => {
expect(result.get('name')).to.equal('Robert\\\'); DROP TABLE "students"; --');
return Student.findAll();
}).then(result => {
expect(result[0].name).to.equal('Robert\\\'); DROP TABLE "students"; --');
});
}); });
expect(result0.get('name')).to.equal('Robert\\\'); DROP TABLE "students"; --');
const result = await Student.findAll();
expect(result[0].name).to.equal('Robert\\\'); DROP TABLE "students"; --');
}); });
}); });
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!