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

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) {
});
describe('context', () => {
it('does not use continuation storage on manually managed transactions', function() {
return Sequelize._clsRun(async () => {
it('does not use continuation storage on manually managed transactions', async function() {
await Sequelize._clsRun(async () => {
const transaction = await this.sequelize.transaction();
expect(this.ns.get('transaction')).not.to.be.ok;
await transaction.rollback();
......@@ -52,14 +52,13 @@ if (current.dialect.supports.transactions) {
expect(t1id).not.to.equal(t2id);
});
it('supports nested promise chains', function() {
return this.sequelize.transaction(() => {
it('supports nested promise chains', async function() {
await this.sequelize.transaction(async () => {
const tid = this.ns.get('transaction').id;
return this.User.findAll().then(() => {
expect(this.ns.get('transaction').id).to.be.ok;
expect(this.ns.get('transaction').id).to.equal(tid);
});
await this.User.findAll();
expect(this.ns.get('transaction').id).to.be.ok;
expect(this.ns.get('transaction').id).to.equal(tid);
});
});
......@@ -100,8 +99,8 @@ if (current.dialect.supports.transactions) {
expect(this.ns.get('transaction')).not.to.be.ok;
});
it('does not leak outside findOrCreate', function() {
return this.User.findOrCreate({
it('does not leak outside findOrCreate', async function() {
await this.User.findOrCreate({
where: {
name: 'Kafka'
},
......@@ -110,26 +109,25 @@ if (current.dialect.supports.transactions) {
throw new Error('The transaction was not properly assigned');
}
}
}).then(() => {
return this.User.findAll();
});
await this.User.findAll();
});
});
describe('sequelize.query integration', () => {
it('automagically uses the transaction in all calls', function() {
return this.sequelize.transaction(() => {
return this.User.create({ name: 'bob' }).then(() => {
return Promise.all([
expect(this.User.findAll({ transaction: null })).to.eventually.have.length(0),
expect(this.User.findAll({})).to.eventually.have.length(1)
]);
});
it('automagically uses the transaction in all calls', async function() {
await this.sequelize.transaction(async () => {
await this.User.create({ name: 'bob' });
return Promise.all([
expect(this.User.findAll({ transaction: null })).to.eventually.have.length(0),
expect(this.User.findAll({})).to.eventually.have.length(1)
]);
});
});
it('automagically uses the transaction in all calls with async/await', function() {
return this.sequelize.transaction(async () => {
it('automagically uses the transaction in all calls with async/await', async function() {
await this.sequelize.transaction(async () => {
await this.User.create({ name: 'bob' });
expect(await this.User.findAll({ transaction: null })).to.have.length(0);
expect(await this.User.findAll({})).to.have.length(1);
......@@ -141,10 +139,11 @@ if (current.dialect.supports.transactions) {
expect(Sequelize._cls).to.equal(this.ns);
});
it('promises returned by sequelize.query are correctly patched', function() {
return this.sequelize.transaction(t =>
this.sequelize.query('select 1', { type: Sequelize.QueryTypes.SELECT })
.then(() => expect(this.ns.get('transaction')).to.equal(t))
it('promises returned by sequelize.query are correctly patched', async function() {
await this.sequelize.transaction(async t => {
await this.sequelize.query('select 1', { type: Sequelize.QueryTypes.SELECT });
return expect(this.ns.get('transaction')).to.equal(t);
}
);
});
});
......
......@@ -17,7 +17,7 @@ if (dialect === 'sqlite') {
describe(Support.getTestDialectTeaser('Configuration'), () => {
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 = {
logging: false,
host: '0.0.0.1',
......@@ -42,10 +42,10 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
}
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') {
// NOTE: Travis seems to be having trouble with this test against the
// AWS instance. Works perfectly fine on a local setup.
......@@ -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 });
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.
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.', () => {
......@@ -70,7 +71,7 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
describe('Instantiation with arguments', () => {
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 createTableFoo = 'CREATE TABLE foo (faz TEXT);';
const createTableBar = 'CREATE TABLE bar (baz TEXT);';
......@@ -79,65 +80,62 @@ describe(Support.getTestDialectTeaser('Configuration'), () => {
return promisify(fs.access)(p, fs.R_OK | fs.W_OK);
};
return promisify(fs.unlink)(p)
.catch(err => {
try {
try {
await promisify(fs.unlink)(p);
} catch (err) {
expect(err.code).to.equal('ENOENT');
})
.then(() => {
const sequelizeReadOnly = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READONLY
}
});
const sequelizeReadWrite = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READWRITE
}
});
expect(sequelizeReadOnly.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READONLY);
expect(sequelizeReadWrite.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READWRITE);
return Promise.all([
sequelizeReadOnly.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file'),
sequelizeReadWrite.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file')
]);
})
.then(() => {
// By default, sqlite creates a connection that's READWRITE | CREATE
const sequelize = new Sequelize('sqlite://foo', {
storage: p
});
return sequelize.query(createTableFoo);
})
.then(testAccess)
.then(() => {
const sequelizeReadOnly = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READONLY
}
});
const sequelizeReadWrite = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READWRITE
}
});
return Promise.all([
sequelizeReadOnly.query(createTableBar)
.should.be.rejectedWith(Error, 'SQLITE_READONLY: attempt to write a readonly database'),
sequelizeReadWrite.query(createTableBar)
]);
})
.finally(() => {
return promisify(fs.unlink)(p);
}
const sequelizeReadOnly0 = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READONLY
}
});
const sequelizeReadWrite0 = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READWRITE
}
});
expect(sequelizeReadOnly0.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READONLY);
expect(sequelizeReadWrite0.config.dialectOptions.mode).to.equal(sqlite3.OPEN_READWRITE);
await Promise.all([
sequelizeReadOnly0.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file'),
sequelizeReadWrite0.query(createTableFoo)
.should.be.rejectedWith(Error, 'SQLITE_CANTOPEN: unable to open database file')
]);
// By default, sqlite creates a connection that's READWRITE | CREATE
const sequelize = new Sequelize('sqlite://foo', {
storage: p
});
await testAccess(await sequelize.query(createTableFoo));
const sequelizeReadOnly = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READONLY
}
});
const sequelizeReadWrite = new Sequelize('sqlite://foo', {
storage: p,
dialectOptions: {
mode: sqlite3.OPEN_READWRITE
}
});
await Promise.all([
sequelizeReadOnly.query(createTableBar)
.should.be.rejectedWith(Error, 'SQLITE_READONLY: attempt to write a readonly database'),
sequelizeReadWrite.query(createTableBar)
]);
} finally {
await promisify(fs.unlink)(p);
}
});
}
});
......
......@@ -21,7 +21,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
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 => {
return moment(value, 'YYYY-MM-DD HH:mm:ss');
});
......@@ -41,23 +41,23 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false
});
return current.sync({ force: true }).then(() => {
return User.create({
dateField: moment('2011 10 31', 'YYYY MM DD')
});
}).then(() => {
return User.findAll().then(obj => obj[0]);
}).then(user => {
expect(parse).to.have.been.called;
expect(stringify).to.have.been.called;
await current.sync({ force: true });
expect(moment.isMoment(user.dateField)).to.be.ok;
delete Sequelize.DATE.parse;
await User.create({
dateField: moment('2011 10 31', 'YYYY MM DD')
});
const obj = await User.findAll();
const user = obj[0];
expect(parse).to.have.been.called;
expect(stringify).to.have.been.called;
expect(moment.isMoment(user.dateField)).to.be.ok;
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 => {
return value;
});
......@@ -78,29 +78,27 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false
});
return current.sync({ force: true }).then(() => {
await current.sync({ force: true });
current.refreshTypes();
return User.create({
field: value
});
}).then(() => {
return User.findAll().then(obj => obj[0]);
}).then(() => {
expect(parse).to.have.been.called;
if (options && options.useBindParam) {
expect(bindParam).to.have.been.called;
} else {
expect(stringify).to.have.been.called;
}
current.refreshTypes();
delete Type.constructor.parse;
delete Type.constructor.prototype.stringify;
if (options && options.useBindParam) {
delete Type.constructor.prototype.bindParam;
}
await User.create({
field: value
});
await User.findAll();
expect(parse).to.have.been.called;
if (options && options.useBindParam) {
expect(bindParam).to.have.been.called;
} else {
expect(stringify).to.have.been.called;
}
delete Type.constructor.parse;
delete Type.constructor.prototype.stringify;
if (options && options.useBindParam) {
delete Type.constructor.prototype.bindParam;
}
};
const testFailure = function(Type) {
......@@ -114,229 +112,231 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
};
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();
return testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
await testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
});
}
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();
return testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
await testSuccess(Type, { test: 42, nested: { foo: 'bar' } });
});
}
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();
return testSuccess(Type, { test: 42, nested: false }, { useBindParam: true });
await testSuccess(Type, { test: 42, nested: false }, { useBindParam: true });
});
}
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());
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();
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();
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();
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();
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();
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();
// 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
// isn't needed for other dialects
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();
if (dialect === 'mssql') {
// Text uses nvarchar, same type as string
testFailure(Type);
} 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();
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();
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();
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();
if (dialect === 'mssql') {
// Same type as integer
testFailure(Type);
} 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', {
age: Sequelize.BIGINT
});
const age = BigInt(Number.MAX_SAFE_INTEGER) * 2n;
return User.sync({ force: true }).then(() => {
return User.create({ age });
}).then(user => {
expect(BigInt(user.age).toString()).to.equal(age.toString());
return User.findAll({
where: { age }
});
}).then(users => {
expect(users).to.have.lengthOf(1);
expect(BigInt(users[0].age).toString()).to.equal(age.toString());
await User.sync({ force: true });
const user = await User.create({ age });
expect(BigInt(user.age).toString()).to.equal(age.toString());
const users = await User.findAll({
where: { age }
});
expect(users).to.have.lengthOf(1);
expect(BigInt(users[0].age).toString()).to.equal(age.toString());
});
if (dialect === 'mysql') {
it('should handle TINYINT booleans', function() {
it('should handle TINYINT booleans', async function() {
const User = this.sequelize.define('user', {
id: { type: Sequelize.TINYINT, primaryKey: true },
isRegistered: Sequelize.TINYINT
});
return User.sync({ force: true }).then(() => {
return User.create({ id: 1, isRegistered: true });
}).then(registeredUser => {
expect(registeredUser.isRegistered).to.equal(true);
return User.findOne({
where: {
id: 1,
isRegistered: true
}
});
}).then(registeredUser => {
expect(registeredUser).to.be.ok;
expect(registeredUser.isRegistered).to.equal(1);
return User.create({ id: 2, isRegistered: false });
}).then(unregisteredUser => {
expect(unregisteredUser.isRegistered).to.equal(false);
return User.findOne({
where: {
id: 2,
isRegistered: false
}
});
}).then(unregisteredUser => {
expect(unregisteredUser).to.be.ok;
expect(unregisteredUser.isRegistered).to.equal(0);
await User.sync({ force: true });
const registeredUser0 = await User.create({ id: 1, isRegistered: true });
expect(registeredUser0.isRegistered).to.equal(true);
const registeredUser = await User.findOne({
where: {
id: 1,
isRegistered: true
}
});
expect(registeredUser).to.be.ok;
expect(registeredUser.isRegistered).to.equal(1);
const unregisteredUser0 = await User.create({ id: 2, isRegistered: false });
expect(unregisteredUser0.isRegistered).to.equal(false);
const unregisteredUser = await User.findOne({
where: {
id: 2,
isRegistered: false
}
});
expect(unregisteredUser).to.be.ok;
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();
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();
if (dialect === 'postgres') {
// Postgres doesn't have float, maps to either decimal or double
testFailure(Type);
} 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();
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();
// there is no dialect.supports.UUID yet
if (['postgres', 'sqlite'].includes(dialect)) {
return testSuccess(Type, uuid.v4());
await testSuccess(Type, uuid.v4());
} else {
// No native uuid type
testFailure(Type);
}
// No native uuid type
testFailure(Type);
});
it('calls parse and stringify for CIDR', () => {
it('calls parse and stringify for CIDR', async () => {
const Type = new Sequelize.CIDR();
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();
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();
if (dialect === 'sqlite') {
......@@ -345,38 +345,41 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
}
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();
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');
if (['postgres'].includes(dialect)) {
return testSuccess(Type, 'hat');
await testSuccess(Type, 'hat');
} else {
testFailure(Type);
}
testFailure(Type);
});
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();
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();
// MySQL 5.7 or above doesn't support POINT EMPTY
......@@ -384,7 +387,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
return;
}
return new Promise((resolve, reject) => {
const runTests = await new Promise((resolve, reject) => {
if (/^postgres/.test(dialect)) {
current.query('SELECT PostGIS_Lib_Version();')
.then(result => {
......@@ -397,37 +400,36 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
} else {
resolve(true);
}
}).then(runTests => {
if (current.dialect.supports.GEOMETRY && runTests) {
current.refreshTypes();
const User = current.define('user', { field: Type }, { timestamps: false });
const point = { type: 'Point', coordinates: [] };
return current.sync({ force: true }).then(() => {
return User.create({
//insert a empty GEOMETRY type
field: point
});
}).then(() => {
//This case throw unhandled exception
return User.findAll();
}).then(users =>{
if (dialect === 'mysql' || dialect === 'mariadb') {
// MySQL will return NULL, because they lack EMPTY geometry data support.
expect(users[0].field).to.be.eql(null);
} else if (dialect === 'postgres' || dialect === 'postgres-native') {
//Empty Geometry data [0,0] as per https://trac.osgeo.org/postgis/ticket/1996
expect(users[0].field).to.be.deep.eql({ type: 'Point', coordinates: [0, 0] });
} else {
expect(users[0].field).to.be.deep.eql(point);
}
});
}
});
if (current.dialect.supports.GEOMETRY && runTests) {
current.refreshTypes();
const User = current.define('user', { field: Type }, { timestamps: false });
const point = { type: 'Point', coordinates: [] };
await current.sync({ force: true });
await User.create({
//insert a empty GEOMETRY type
field: point
});
//This case throw unhandled exception
const users = await User.findAll();
if (dialect === 'mysql' || dialect === 'mariadb') {
// MySQL will return NULL, because they lack EMPTY geometry data support.
expect(users[0].field).to.be.eql(null);
} else if (dialect === 'postgres' || dialect === 'postgres-native') {
//Empty Geometry data [0,0] as per https://trac.osgeo.org/postgis/ticket/1996
expect(users[0].field).to.be.deep.eql({ type: 'Point', coordinates: [0, 0] });
} else {
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();
current.refreshTypes();
......@@ -435,48 +437,46 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const User = current.define('user', { field: Type }, { timestamps: false });
const point = null;
return current.sync({ force: true }).then(() => {
return User.create({
// insert a null GEOMETRY type
field: point
});
}).then(() => {
//This case throw unhandled exception
return User.findAll();
}).then(users =>{
expect(users[0].field).to.be.eql(null);
await current.sync({ force: true });
await User.create({
// insert a null GEOMETRY type
field: point
});
//This case throw unhandled exception
const users = await User.findAll();
expect(users[0].field).to.be.eql(null);
});
}
if (dialect === 'postgres' || dialect === 'sqlite') {
// 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', {
float: Sequelize.FLOAT,
double: Sequelize.DOUBLE,
real: Sequelize.REAL
});
return Model.sync({ force: true }).then(() => {
return Model.create({
id: 1,
float: NaN,
double: Infinity,
real: -Infinity
});
}).then(() => {
return Model.findOne({ where: { id: 1 } });
}).then(user => {
expect(user.get('float')).to.be.NaN;
expect(user.get('double')).to.eq(Infinity);
expect(user.get('real')).to.eq(-Infinity);
await Model.sync({ force: true });
await Model.create({
id: 1,
float: NaN,
double: Infinity,
real: -Infinity
});
const user = await Model.findOne({ where: { id: 1 } });
expect(user.get('float')).to.be.NaN;
expect(user.get('double')).to.eq(Infinity);
expect(user.get('real')).to.eq(-Infinity);
});
}
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', {
decimal: Sequelize.DECIMAL,
decimalPre: Sequelize.DECIMAL(10, 4),
......@@ -494,31 +494,28 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
decimalWithFloatParser: 0.12345678
};
return Model.sync({ force: true }).then(() => {
return Model.create(sampleData);
}).then(() => {
return Model.findByPk(1);
}).then(user => {
/**
* MYSQL default precision is 10 and scale is 0
* Thus test case below will return number without any fraction values
*/
if (dialect === 'mysql') {
expect(user.get('decimal')).to.be.eql('12345678');
} else {
expect(user.get('decimal')).to.be.eql('12345678.12345678');
}
await Model.sync({ force: true });
await Model.create(sampleData);
const user = await Model.findByPk(1);
/**
* MYSQL default precision is 10 and scale is 0
* Thus test case below will return number without any fraction values
*/
if (dialect === 'mysql') {
expect(user.get('decimal')).to.be.eql('12345678');
} else {
expect(user.get('decimal')).to.be.eql('12345678.12345678');
}
expect(user.get('decimalPre')).to.be.eql('123456.1234');
expect(user.get('decimalWithParser')).to.be.eql('12345678123456781.123456781234567');
expect(user.get('decimalWithIntParser')).to.be.eql('1.2340');
expect(user.get('decimalWithFloatParser')).to.be.eql('0.12345678');
});
expect(user.get('decimalPre')).to.be.eql('123456.1234');
expect(user.get('decimalWithParser')).to.be.eql('12345678123456781.123456781234567');
expect(user.get('decimalWithIntParser')).to.be.eql('1.2340');
expect(user.get('decimalWithFloatParser')).to.be.eql('0.12345678');
});
}
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', {
jewelPurity: Sequelize.BIGINT
});
......@@ -528,19 +525,16 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
jewelPurity: '9223372036854775807'
};
return Model.sync({ force: true }).then(() => {
return Model.create(sampleData);
}).then(() => {
return Model.findByPk(1);
}).then(user => {
expect(user.get('jewelPurity')).to.be.eql(sampleData.jewelPurity);
expect(user.get('jewelPurity')).to.be.string;
});
await Model.sync({ force: true });
await Model.create(sampleData);
const user = await Model.findByPk(1);
expect(user.get('jewelPurity')).to.be.eql(sampleData.jewelPurity);
expect(user.get('jewelPurity')).to.be.string;
});
}
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', {
interval: {
type: Sequelize.RANGE(Sequelize.INTEGER),
......@@ -549,19 +543,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
}
});
return Model.sync({ force: true })
.then(() => Model.create({ interval: [1, 4] }) )
.then(() => Model.findAll() )
.then(([m]) => {
expect(m.interval[0].value).to.be.eql(1);
expect(m.interval[1].value).to.be.eql(4);
});
await Model.sync({ force: true });
await Model.create({ interval: [1, 4] });
const [m] = await Model.findAll();
expect(m.interval[0].value).to.be.eql(1);
expect(m.interval[1].value).to.be.eql(4);
});
}
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', {
interval: {
type: Sequelize.RANGE(Sequelize.DATE),
......@@ -573,19 +565,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [testDate1, testDate2];
return Model.sync({ force: true })
.then(() => Model.create({ interval: testDateRange }))
.then(() => Model.findOne())
.then(m => {
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(true);
expect(m.interval[1].inclusive).to.be.eql(false);
});
await Model.sync({ force: true });
await Model.create({ interval: testDateRange });
const m = await Model.findOne();
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(true);
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', {
interval: {
type: Sequelize.RANGE(Sequelize.DATE),
......@@ -597,19 +587,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [{ value: testDate1, inclusive: false }, { value: testDate2, inclusive: true }];
return Model.sync({ force: true })
.then(() => Model.create({ interval: testDateRange }))
.then(() => Model.findOne())
.then(m => {
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(false);
expect(m.interval[1].inclusive).to.be.eql(true);
});
await Model.sync({ force: true });
await Model.create({ interval: testDateRange });
const m = await Model.findOne();
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(false);
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', {
interval: {
type: Sequelize.RANGE(Sequelize.DATE),
......@@ -621,19 +609,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDate2 = new Date(testDate1.getTime() + 10000);
const testDateRange = [testDate1, { value: testDate2, inclusive: true }];
return Model.sync({ force: true })
.then(() => Model.create({ interval: testDateRange }))
.then(() => Model.findOne())
.then(m => {
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].inclusive).to.be.eql(true);
expect(m.interval[1].inclusive).to.be.eql(true);
});
await Model.sync({ force: true });
await Model.create({ interval: testDateRange });
const m = await Model.findOne();
expect(m).to.exist;
expect(m.interval[0].value).to.be.eql(testDate1);
expect(m.interval[1].value).to.be.eql(testDate2);
expect(m.interval[0].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', {
interval: {
type: Sequelize.RANGE(Sequelize.DATE),
......@@ -646,101 +632,90 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
const testDateRange = [testDate1, testDate2];
const dateRangePredicate = [{ value: testDate1, inclusive: true }, { value: testDate1, inclusive: true }];
return Model.sync({ force: true })
.then(() => Model.create({ interval: testDateRange }))
.then(() => Model.findOne({
where: {
interval: { [Op.overlap]: dateRangePredicate }
}
}))
.then(m => {
expect(m).to.exist;
});
await Model.sync({ force: true });
await Model.create({ interval: testDateRange });
const m = await Model.findOne({
where: {
interval: { [Op.overlap]: dateRangePredicate }
}
});
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', {
name: Sequelize.STRING,
type: Sequelize.ENUM(['action', 'mecha', 'canon', 'class s'])
});
return Model.sync({ force: true }).then(() => {
return Model.create({ name: 'sakura', type: 'class s' });
}).then(record => {
expect(record.type).to.be.eql('class s');
});
await Model.sync({ force: true });
const record = await Model.create({ name: 'sakura', type: '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', {
stamp: Sequelize.DATEONLY
});
const testDate = moment().format('YYYY-MM-DD');
const newDate = new Date();
return Model.sync({ force: true })
.then(() => Model.create({ stamp: testDate }))
.then(record => {
expect(typeof record.stamp).to.be.eql('string');
expect(record.stamp).to.be.eql(testDate);
await Model.sync({ force: true });
const record4 = await Model.create({ stamp: testDate });
expect(typeof record4.stamp).to.be.eql('string');
expect(record4.stamp).to.be.eql(testDate);
return Model.findByPk(record.id);
}).then(record => {
expect(typeof record.stamp).to.be.eql('string');
expect(record.stamp).to.be.eql(testDate);
const record3 = await Model.findByPk(record4.id);
expect(typeof record3.stamp).to.be.eql('string');
expect(record3.stamp).to.be.eql(testDate);
return record.update({
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({
stamp: newDate
});
}).then(record => {
return record.reload();
}).then(record => {
expect(typeof record.stamp).to.be.eql('string');
const recordDate = new Date(record.stamp);
expect(recordDate.getUTCFullYear()).to.equal(newDate.getUTCFullYear());
expect(recordDate.getUTCDate()).to.equal(newDate.getUTCDate());
expect(recordDate.getUTCMonth()).to.equal(newDate.getUTCMonth());
});
const record2 = await record3.update({
stamp: testDate
});
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
});
const record = await record0.reload();
expect(typeof record.stamp).to.be.eql('string');
const recordDate = new Date(record.stamp);
expect(recordDate.getUTCFullYear()).to.equal(newDate.getUTCFullYear());
expect(recordDate.getUTCDate()).to.equal(newDate.getUTCDate());
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', {
stamp: Sequelize.DATEONLY
});
const testDate = moment().format('YYYY-MM-DD');
return Model.sync({ force: true })
.then(() => Model.create({ stamp: testDate }))
.then(record => {
expect(typeof record.stamp).to.be.eql('string');
expect(record.stamp).to.be.eql(testDate);
await Model.sync({ force: true });
const record2 = await Model.create({ stamp: testDate });
expect(typeof record2.stamp).to.be.eql('string');
expect(record2.stamp).to.be.eql(testDate);
return Model.findByPk(record.id);
}).then(record => {
expect(typeof record.stamp).to.be.eql('string');
expect(record.stamp).to.be.eql(testDate);
const record1 = await Model.findByPk(record2.id);
expect(typeof record1.stamp).to.be.eql('string');
expect(record1.stamp).to.be.eql(testDate);
return record.update({
stamp: null
});
}).then(record => {
return record.reload();
}).then(record => {
expect(record.stamp).to.be.eql(null);
});
const record0 = await record1.update({
stamp: null
});
const record = await record0.reload();
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', {
byteToBool: this.sequelize.Sequelize.BLOB
}, {
......@@ -753,18 +728,17 @@ describe(Support.getTestDialectTeaser('DataTypes'), () => {
timestamps: false
});
return ByteModel.sync({
await ByteModel.sync({
force: true
}).then(() => {
return ByteModel.create({
byteToBool: Buffer.from([true])
});
}).then(byte => {
expect(byte.byteToBool).to.be.ok;
});
return BoolModel.findByPk(byte.id);
}).then(bool => {
expect(bool.byteToBool).to.be.true;
const byte = await ByteModel.create({
byteToBool: Buffer.from([true])
});
expect(byte.byteToBool).to.be.ok;
const bool = await BoolModel.findByPk(byte.id);
expect(bool.byteToBool).to.be.true;
});
});
......@@ -255,7 +255,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
});
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', {
number: {
type: Sequelize.INTEGER
......@@ -264,22 +264,21 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
version: true
});
return Account.sync({ force: true }).then(() => {
const result = Account.create({ number: 1 }).then(accountA => {
return Account.findByPk(accountA.id).then(accountB => {
accountA.number += 1;
return accountA.save().then(() => { return accountB; });
});
}).then(accountB => {
accountB.number += 1;
return accountB.save();
});
return Promise.all([
expect(result).to.eventually.be.rejectedWith(Support.Sequelize.OptimisticLockError),
expect(result).to.eventually.be.rejectedWith('Attempting to update a stale model instance: Account')
]);
});
await Account.sync({ force: true });
const result = (async () => {
const accountA = await Account.create({ number: 1 });
const accountB0 = await Account.findByPk(accountA.id);
accountA.number += 1;
await accountA.save();
const accountB = await accountB0;
accountB.number += 1;
return await accountB.save();
})();
await Promise.all([
expect(result).to.eventually.be.rejectedWith(Support.Sequelize.OptimisticLockError),
expect(result).to.eventually.be.rejectedWith('Attempting to update a stale model instance: Account')
]);
});
});
......@@ -295,7 +294,7 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}
].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(),
User = this.sequelize.define('user', {
first_name: {
......@@ -309,21 +308,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
});
const record = { first_name: 'jan', last_name: 'meier' };
return this.sequelize.sync({ force: true }).then(() => {
return User.create(record);
}).then(() => {
return User.create(record).catch(err => {
if (!(err instanceof constraintTest.exception)) throw err;
return spy(err);
});
}).then(() => {
expect(spy).to.have.been.calledOnce;
});
await this.sequelize.sync({ force: true });
await User.create(record);
try {
await User.create(record);
} catch (err) {
if (!(err instanceof constraintTest.exception)) throw err;
await spy(err);
}
expect(spy).to.have.been.calledOnce;
});
});
it('Supports newlines in keys', function() {
it('Supports newlines in keys', async function() {
const spy = sinon.spy(),
User = this.sequelize.define('user', {
name: {
......@@ -332,20 +332,20 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}
});
return this.sequelize.sync({ force: true }).then(() => {
return User.create({ name: 'jan' });
}).then(() => {
// If the error was successfully parsed, we can catch it!
return User.create({ name: 'jan' }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
return spy(err);
});
}).then(() => {
expect(spy).to.have.been.calledOnce;
});
await this.sequelize.sync({ force: true });
await User.create({ name: 'jan' });
try {
await User.create({ name: 'jan' });
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
await spy(err);
}
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', {
name: {
type: Sequelize.STRING,
......@@ -353,23 +353,21 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}
}, { timestamps: false });
return this.sequelize.sync({ force: true }).then(() => {
// Now let's pretend the index was created by someone else, and sequelize doesn't know about it
User = this.sequelize.define('user', {
name: Sequelize.STRING
}, { timestamps: false });
return User.create({ name: 'jan' });
}).then(() => {
// 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);
}).then(() => {
// 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 this.sequelize.sync({ force: true });
// Now let's pretend the index was created by someone else, and sequelize doesn't know about it
User = this.sequelize.define('user', {
name: Sequelize.STRING
}, { timestamps: false });
await User.create({ name: 'jan' });
// It should work even though the unique key is not defined in the model
await expect(User.create({ name: 'jan' })).to.be.rejectedWith(Sequelize.UniqueConstraintError);
// And when the model is not passed at all
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', {
name: {
type: Sequelize.STRING,
......@@ -377,28 +375,22 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
}
}, { timestamps: false });
return this.sequelize.sync({ force: true })
.then(() => {
return User.create({ name: 'jan' });
}).then(() => {
// Unique key
return expect(User.create({ name: 'jan' })).to.be.rejected;
}).then(error => {
expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error).to.have.property('parent');
expect(error).to.have.property('original');
expect(error).to.have.property('sql');
return User.create({ id: 2, name: 'jon' });
}).then(() => {
// Primary key
return expect(User.create({ id: 2, name: 'jon' })).to.be.rejected;
}).then(error => {
expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error).to.have.property('parent');
expect(error).to.have.property('original');
expect(error).to.have.property('sql');
});
await this.sequelize.sync({ force: true });
await User.create({ name: 'jan' });
// Unique key
const error0 = await expect(User.create({ name: 'jan' })).to.be.rejected;
expect(error0).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error0).to.have.property('parent');
expect(error0).to.have.property('original');
expect(error0).to.have.property('sql');
await User.create({ id: 2, name: 'jon' });
// Primary key
const error = await expect(User.create({ id: 2, name: 'jon' })).to.be.rejected;
expect(error).to.be.instanceOf(Sequelize.UniqueConstraintError);
expect(error).to.have.property('parent');
expect(error).to.have.property('original');
expect(error).to.have.property('sql');
});
});
});
......@@ -16,149 +16,144 @@ const sortById = function(a, b) {
describe(Support.getTestDialectTeaser('Include'), () => {
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', {}),
User = this.sequelize.define('User', {});
User.belongsTo(Company, { as: 'Employer' });
return this.sequelize.sync({ force: true }).then(() => {
return User.create();
}).then(() => {
return User.findOne({
include: [{ model: Company, as: 'Employer' }]
}).then(user => {
expect(user).to.be.ok;
});
await this.sequelize.sync({ force: true });
await User.create();
const user = await User.findOne({
include: [{ model: Company, as: 'Employer' }]
});
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', {}),
User = this.sequelize.define('User', {}),
Employer = User.belongsTo(Company, { as: 'Employer' });
return this.sequelize.sync({ force: true }).then(() => {
return User.create();
}).then(() => {
return User.findOne({
include: [Employer]
}).then(user => {
expect(user).to.be.ok;
});
await this.sequelize.sync({ force: true });
await User.create();
const user = await User.findOne({
include: [Employer]
});
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 Table2 = this.sequelize.define('Table2');
const Table3 = this.sequelize.define('Table3', { value: DataTypes.INTEGER });
Table1.hasOne(Table2, { foreignKey: 'Table1Id' });
Table2.hasMany(Table3, { as: 'Tables3', foreignKey: 'Table2Id' });
return this.sequelize.sync({ force: true }).then(() => {
return Table1.create().then(table1 => {
return Table2.create({
Table1Id: table1.get('id')
});
}).then(table2 => {
return Table3.bulkCreate([
{
Table2Id: table2.get('id'),
value: 5
},
{
Table2Id: table2.get('id'),
value: 7
}
], {
validate: true
});
});
}).then(() => {
return Table1.findAll({
raw: true,
attributes: [
[Sequelize.fn('SUM', Sequelize.col('Table2.Tables3.value')), 'sum']
],
include: [
{
model: Table2,
attributes: [],
include: [
{
model: Table3,
as: 'Tables3',
attributes: []
}
]
}
]
}).then(result => {
expect(result.length).to.equal(1);
expect(parseInt(result[0].sum, 10)).to.eq(12);
});
await this.sequelize.sync({ force: true });
const table1 = await Table1.create();
const table2 = await Table2.create({
Table1Id: table1.get('id')
});
await Table3.bulkCreate([
{
Table2Id: table2.get('id'),
value: 5
},
{
Table2Id: table2.get('id'),
value: 7
}
], {
validate: true
});
const result = await Table1.findAll({
raw: true,
attributes: [
[Sequelize.fn('SUM', Sequelize.col('Table2.Tables3.value')), 'sum']
],
include: [
{
model: Table2,
attributes: [],
include: [
{
model: Table3,
as: 'Tables3',
attributes: []
}
]
}
]
});
expect(result.length).to.equal(1);
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 }),
User = this.sequelize.define('User', {}),
Employer = User.belongsTo(Company, { as: 'Employer', foreignKey: 'employerId' });
return this.sequelize.sync({ force: true }).then(() => {
return Company.create({
name: 'CyberCorp'
}).then(company => {
return User.create({
employerId: company.get('id')
});
});
}).then(() => {
return User.findOne({
include: [
{ association: Employer, where: { name: 'CyberCorp' } }
]
}).then(user => {
expect(user).to.be.ok;
});
await this.sequelize.sync({ force: true });
const company = await Company.create({
name: 'CyberCorp'
});
await User.create({
employerId: company.get('id')
});
const user = await User.findOne({
include: [
{ association: Employer, where: { name: 'CyberCorp' } }
]
});
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', {}),
Person = this.sequelize.define('Person', {});
Company.hasOne(Person, { as: 'CEO' });
return this.sequelize.sync({ force: true }).then(() => {
return Company.create().then(() => {
return Company.findOne({
include: [{ model: Person, as: 'CEO' }]
}).then(company => {
expect(company).to.be.ok;
});
});
await this.sequelize.sync({ force: true });
await Company.create();
const company = await Company.findOne({
include: [{ model: Person, as: 'CEO' }]
});
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', {}),
Person = this.sequelize.define('Person', {}),
CEO = Company.hasOne(Person, { as: 'CEO' });
return this.sequelize.sync({ force: true }).then(() => {
return Company.create();
}).then(() => {
return Company.findOne({
include: [CEO]
});
}).then(user => {
expect(user).to.be.ok;
await this.sequelize.sync({ force: true });
await Company.create();
const user = await Company.findOne({
include: [CEO]
});
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', {}),
Person = this.sequelize.define('Person', {});
......@@ -166,91 +161,84 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Employer: Person.belongsTo(Company, { as: 'employer' })
};
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([Person.create(), Company.create()]).then(([person, company]) => {
return person.setEmployer(company);
});
}).then(() => {
return Person.findOne({
include: [Person.relation.Employer]
}).then(person => {
expect(person).to.be.ok;
expect(person.employer).to.be.ok;
});
await this.sequelize.sync({ force: true });
const [person0, company] = await Promise.all([Person.create(), Company.create()]);
await person0.setEmployer(company);
const person = await Person.findOne({
include: [Person.relation.Employer]
});
expect(person).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', {}),
Task = this.sequelize.define('task', {}),
Tasks = User.hasMany(Task);
Task.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => {
return User.create().then(user => {
return user.createTask();
}).then(() => {
return User.findOne({
include: [Tasks]
});
}).then(user => {
expect(user).to.be.ok;
expect(user.tasks).to.be.ok;
});
await this.sequelize.sync({ force: true });
const user0 = await User.create();
await user0.createTask();
const user = await User.findOne({
include: [Tasks]
});
expect(user).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', {}),
Task = this.sequelize.define('task', { title: DataTypes.STRING }),
Tasks = User.hasMany(Task);
Task.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => {
return User.create().then(user => {
return Promise.all([user.createTask({
title: 'trivial'
}), user.createTask({
title: 'pursuit'
})]);
}).then(() => {
return User.findOne({
include: [
{ association: Tasks, where: { title: 'trivial' } }
]
});
}).then(user => {
expect(user).to.be.ok;
expect(user.tasks).to.be.ok;
expect(user.tasks.length).to.equal(1);
});
await this.sequelize.sync({ force: true });
const user0 = await User.create();
await Promise.all([user0.createTask({
title: 'trivial'
}), user0.createTask({
title: 'pursuit'
})]);
const user = await User.findOne({
include: [
{ association: Tasks, where: { title: 'trivial' } }
]
});
expect(user).to.be.ok;
expect(user.tasks).to.be.ok;
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', {}),
Group = this.sequelize.define('group', {}),
Groups = User.belongsToMany(Group, { through: 'UserGroup' });
Group.belongsToMany(User, { through: 'UserGroup' });
return this.sequelize.sync({ force: true }).then(() => {
return User.create().then(user => {
return user.createGroup();
});
}).then(() => {
return User.findOne({
include: [Groups]
}).then(user => {
expect(user).to.be.ok;
expect(user.groups).to.be.ok;
});
await this.sequelize.sync({ force: true });
const user0 = await User.create();
await user0.createGroup();
const user = await User.findOne({
include: [Groups]
});
expect(user).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', {}),
User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {});
......@@ -258,32 +246,33 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Task.belongsTo(User);
User.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => {
return promiseProps({
task: Task.create(),
user: User.create(),
group: Group.create()
}).then(props => {
return Promise.all([props.task.setUser(props.user), props.user.setGroup(props.group)]).then(() => props);
}).then(props => {
return Task.findOne({
where: {
id: props.task.id
},
include: [
{ model: User, include: [
{ model: Group }
] }
]
}).then(task => {
expect(task.User).to.be.ok;
expect(task.User.Group).to.be.ok;
});
});
await this.sequelize.sync({ force: true });
const props0 = await promiseProps({
task: Task.create(),
user: User.create(),
group: Group.create()
});
await Promise.all([props0.task.setUser(props0.user), props0.user.setGroup(props0.group)]);
const props = props0;
const task = await Task.findOne({
where: {
id: props.task.id
},
include: [
{ model: User, include: [
{ model: Group }
] }
]
});
expect(task.User).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', {}),
User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {});
......@@ -291,30 +280,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Task.belongsTo(User);
Task.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => {
return Task.create({
User: {},
Group: {}
}, {
include: [User, Group]
});
}).then(task => {
return Task.findOne({
where: {
id: task.id
},
include: [
{ model: User },
{ model: Group }
]
});
}).then(task => {
expect(task.User).to.be.ok;
expect(task.Group).to.be.ok;
await this.sequelize.sync({ force: true });
const task0 = await Task.create({
User: {},
Group: {}
}, {
include: [User, Group]
});
const task = await Task.findOne({
where: {
id: task0.id
},
include: [
{ model: User },
{ model: Group }
]
});
expect(task.User).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', {}),
User = this.sequelize.define('User', {}),
Group = this.sequelize.define('Group', {});
......@@ -323,31 +312,31 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Group.hasOne(User);
User.belongsTo(Group);
return this.sequelize.sync({ force: true }).then(() => {
return User.create({
Task: {},
Group: {}
}, {
include: [Task, Group]
});
}).then(user => {
return Group.findOne({
where: {
id: user.Group.id
},
include: [
{ model: User, include: [
{ model: Task }
] }
]
});
}).then(group => {
expect(group.User).to.be.ok;
expect(group.User.Task).to.be.ok;
await this.sequelize.sync({ force: true });
const user = await User.create({
Task: {},
Group: {}
}, {
include: [Task, Group]
});
const group = await Group.findOne({
where: {
id: user.Group.id
},
include: [
{ model: User, include: [
{ model: Task }
] }
]
});
expect(group.User).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', {}),
User = this.sequelize.define('User', {}),
Project = this.sequelize.define('Project', {});
......@@ -355,41 +344,40 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.hasMany(Task);
Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => {
return Project.bulkCreate([{ id: 1 }, { id: 2 }]);
}).then(() => {
return User.create({
Tasks: [
{ ProjectId: 1 },
{ ProjectId: 2 },
{ ProjectId: 1 },
{ ProjectId: 2 }
]
}, {
include: [Task]
});
}).then(user => {
return User.findOne({
where: {
id: user.id
},
include: [
{ model: Task, include: [
{ model: Project }
] }
]
});
}).then(user => {
expect(user.Tasks).to.be.ok;
expect(user.Tasks.length).to.equal(4);
await this.sequelize.sync({ force: true });
await Project.bulkCreate([{ id: 1 }, { id: 2 }]);
user.Tasks.forEach(task => {
expect(task.Project).to.be.ok;
});
const user0 = await User.create({
Tasks: [
{ ProjectId: 1 },
{ ProjectId: 2 },
{ ProjectId: 1 },
{ ProjectId: 2 }
]
}, {
include: [Task]
});
const user = await User.findOne({
where: {
id: user0.id
},
include: [
{ model: Task, include: [
{ model: Project }
] }
]
});
expect(user.Tasks).to.be.ok;
expect(user.Tasks.length).to.equal(4);
user.Tasks.forEach(task => {
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', {}),
Worker = this.sequelize.define('Worker', {}),
Project = this.sequelize.define('Project', {});
......@@ -398,32 +386,32 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Project.hasMany(Worker);
Project.hasMany(Task);
return this.sequelize.sync({ force: true }).then(() => {
return Project.create({
Workers: [{}],
Tasks: [{}, {}, {}, {}]
}, {
include: [Worker, Task]
});
}).then(project => {
return Worker.findOne({
where: {
id: project.Workers[0].id
},
include: [
{ model: Project, include: [
{ model: Task }
] }
]
});
}).then(worker => {
expect(worker.Project).to.be.ok;
expect(worker.Project.Tasks).to.be.ok;
expect(worker.Project.Tasks.length).to.equal(4);
await this.sequelize.sync({ force: true });
const project = await Project.create({
Workers: [{}],
Tasks: [{}, {}, {}, {}]
}, {
include: [Worker, Task]
});
const worker = await Worker.findOne({
where: {
id: project.Workers[0].id
},
include: [
{ model: Project, include: [
{ model: Task }
] }
]
});
expect(worker.Project).to.be.ok;
expect(worker.Project.Tasks).to.be.ok;
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', {}),
Product = this.sequelize.define('Product', {
title: DataTypes.STRING
......@@ -436,60 +424,60 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Product.belongsToMany(Tag, { through: 'product_tag' });
Tag.belongsToMany(Product, { through: 'product_tag' });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({
id: 1,
Products: [
{ title: 'Chair' },
{ title: 'Desk' },
{ title: 'Dress' },
{ title: 'Bed' }
]
}, {
include: [Product]
}).then(() => {
return Product.findAll({ order: [['id']] });
}),
Tag.bulkCreate([
{ name: 'A' },
{ name: 'B' },
{ name: 'C' }
]).then(() => {
return Tag.findAll({ order: [['id']] });
})
]);
}).then(([products, tags]) => {
return Promise.all([
products[0].setTags([tags[0], tags[2]]),
products[1].setTags([tags[1]]),
products[2].setTags([tags[0], tags[1], tags[2]])
]);
}).then(() => {
return User.findOne({
where: {
id: 1
},
include: [
{ model: Product, include: [
{ model: Tag }
] }
],
order: [
User.rawAttributes.id,
[Product, 'id']
await this.sequelize.sync({ force: true });
const [products, tags] = await Promise.all([
User.create({
id: 1,
Products: [
{ title: 'Chair' },
{ title: 'Desk' },
{ title: 'Dress' },
{ title: 'Bed' }
]
});
}).then(user => {
expect(user.Products.length).to.equal(4);
expect(user.Products[0].Tags.length).to.equal(2);
expect(user.Products[1].Tags.length).to.equal(1);
expect(user.Products[2].Tags.length).to.equal(3);
expect(user.Products[3].Tags.length).to.equal(0);
}, {
include: [Product]
}).then(() => {
return Product.findAll({ order: [['id']] });
}),
Tag.bulkCreate([
{ name: 'A' },
{ name: 'B' },
{ name: 'C' }
]).then(() => {
return Tag.findAll({ order: [['id']] });
})
]);
await Promise.all([
products[0].setTags([tags[0], tags[2]]),
products[1].setTags([tags[1]]),
products[2].setTags([tags[0], tags[1], tags[2]])
]);
const user = await User.findOne({
where: {
id: 1
},
include: [
{ model: Product, include: [
{ model: Tag }
] }
],
order: [
User.rawAttributes.id,
[Product, 'id']
]
});
expect(user.Products.length).to.equal(4);
expect(user.Products[0].Tags.length).to.equal(2);
expect(user.Products[1].Tags.length).to.equal(1);
expect(user.Products[2].Tags.length).to.equal(3);
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', {}),
Product = this.sequelize.define('Product', {
title: DataTypes.STRING
......@@ -534,78 +522,78 @@ describe(Support.getTestDialectTeaser('Include'), () => {
GroupMember.belongsTo(Group);
Group.hasMany(GroupMember, { as: 'Memberships' });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
Product.create({
id: 1,
title: 'Chair',
Prices: [{ value: 5 }, { value: 10 }]
}, { include: [Price] }),
Product.create({
id: 2,
title: 'Desk',
Prices: [{ value: 5 }, { value: 10 }, { value: 15 }, { value: 20 }]
}, { include: [Price] }),
User.create({
id: 1,
Memberships: [
{ id: 1, Group: { name: 'Developers' }, Rank: { name: 'Admin', canInvite: 1, canRemove: 1 } },
{ id: 2, Group: { name: 'Designers' }, Rank: { name: 'Member', canInvite: 1, canRemove: 0 } }
]
}, {
include: { model: GroupMember, as: 'Memberships', include: [Group, Rank] }
}),
Tag.bulkCreate([
{ name: 'A' },
{ name: 'B' },
{ name: 'C' }
]).then(() => {
return Tag.findAll();
})
]);
}).then(([product1, product2, user, tags]) => {
return Promise.all([
user.setProducts([product1, product2]),
product1.setTags([tags[0], tags[2]]),
product2.setTags([tags[1]]),
product1.setCategory(tags[1])
]);
}).then(() => {
return User.findOne({
where: { id: 1 },
include: [
{ model: GroupMember, as: 'Memberships', include: [
Group,
Rank
] },
{ model: Product, include: [
Tag,
{ model: Tag, as: 'Category' },
Price
] }
await this.sequelize.sync({ force: true });
const [product1, product2, user0, tags] = await Promise.all([
Product.create({
id: 1,
title: 'Chair',
Prices: [{ value: 5 }, { value: 10 }]
}, { include: [Price] }),
Product.create({
id: 2,
title: 'Desk',
Prices: [{ value: 5 }, { value: 10 }, { value: 15 }, { value: 20 }]
}, { include: [Price] }),
User.create({
id: 1,
Memberships: [
{ id: 1, Group: { name: 'Developers' }, Rank: { name: 'Admin', canInvite: 1, canRemove: 1 } },
{ id: 2, Group: { name: 'Designers' }, Rank: { name: 'Member', canInvite: 1, canRemove: 0 } }
]
});
}).then(user => {
user.Memberships.sort(sortById);
expect(user.Memberships.length).to.equal(2);
expect(user.Memberships[0].Group.name).to.equal('Developers');
expect(user.Memberships[0].Rank.canRemove).to.equal(1);
expect(user.Memberships[1].Group.name).to.equal('Designers');
expect(user.Memberships[1].Rank.canRemove).to.equal(0);
user.Products.sort(sortById);
expect(user.Products.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[0].Category).to.be.ok;
expect(user.Products[1].Category).not.to.be.ok;
expect(user.Products[0].Prices.length).to.equal(2);
expect(user.Products[1].Prices.length).to.equal(4);
}, {
include: { model: GroupMember, as: 'Memberships', include: [Group, Rank] }
}),
Tag.bulkCreate([
{ name: 'A' },
{ name: 'B' },
{ name: 'C' }
]).then(() => {
return Tag.findAll();
})
]);
await Promise.all([
user0.setProducts([product1, product2]),
product1.setTags([tags[0], tags[2]]),
product2.setTags([tags[1]]),
product1.setCategory(tags[1])
]);
const user = await User.findOne({
where: { id: 1 },
include: [
{ model: GroupMember, as: 'Memberships', include: [
Group,
Rank
] },
{ model: Product, include: [
Tag,
{ model: Tag, as: 'Category' },
Price
] }
]
});
user.Memberships.sort(sortById);
expect(user.Memberships.length).to.equal(2);
expect(user.Memberships[0].Group.name).to.equal('Developers');
expect(user.Memberships[0].Rank.canRemove).to.equal(1);
expect(user.Memberships[1].Group.name).to.equal('Designers');
expect(user.Memberships[1].Rank.canRemove).to.equal(0);
user.Products.sort(sortById);
expect(user.Products.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[0].Category).to.be.ok;
expect(user.Products[1].Category).not.to.be.ok;
expect(user.Products[0].Prices.length).to.equal(2);
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', {
title: Sequelize.STRING
});
......@@ -618,30 +606,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Project.hasMany(Task);
Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => {
return Task.create({
title: 'FooBar',
Project: { title: 'BarFoo' }
}, {
include: [Project]
});
}).then(() => {
return Task.findAll({
attributes: ['title'],
include: [
{ model: Project, attributes: ['title'] }
]
});
}).then(tasks => {
expect(tasks[0].title).to.equal('FooBar');
expect(tasks[0].Project.title).to.equal('BarFoo');
await this.sequelize.sync({ force: true });
await Task.create({
title: 'FooBar',
Project: { title: 'BarFoo' }
}, {
include: [Project]
});
expect(_.omit(tasks[0].get(), 'Project')).to.deep.equal({ title: 'FooBar' });
expect(tasks[0].Project.get()).to.deep.equal({ title: 'BarFoo' });
const tasks = await Task.findAll({
attributes: ['title'],
include: [
{ model: Project, attributes: ['title'] }
]
});
expect(tasks[0].title).to.equal('FooBar');
expect(tasks[0].Project.title).to.equal('BarFoo');
expect(_.omit(tasks[0].get(), 'Project')).to.deep.equal({ title: 'FooBar' });
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 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
......@@ -650,75 +638,71 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Post.hasMany(PostComment);
return this.sequelize.sync({ force: true }).then(() => {
return Post.create({});
}).then(post => {
return post.createPostComment({
comment_title: 'WAT'
});
}).then(() => {
let findAttributes;
if (dialect === 'mssql') {
findAttributes = [
Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "PostComments.someProperty"'),
[Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT)'), 'someProperty2']
];
} else {
findAttributes = [
Sequelize.literal('EXISTS(SELECT 1) AS "PostComments.someProperty"'),
[Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2']
];
}
findAttributes.push(['comment_title', 'commentTitle']);
return Post.findAll({
include: [
{
model: PostComment,
attributes: findAttributes
}
]
});
}).then(posts => {
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('commentTitle')).to.equal('WAT');
await this.sequelize.sync({ force: true });
const post = await Post.create({});
await post.createPostComment({
comment_title: 'WAT'
});
let findAttributes;
if (dialect === 'mssql') {
findAttributes = [
Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "PostComments.someProperty"'),
[Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT)'), 'someProperty2']
];
} else {
findAttributes = [
Sequelize.literal('EXISTS(SELECT 1) AS "PostComments.someProperty"'),
[Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2']
];
}
findAttributes.push(['comment_title', 'commentTitle']);
const posts = await Post.findAll({
include: [
{
model: PostComment,
attributes: findAttributes
}
]
});
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('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', {
name: DataTypes.STRING
});
Group.belongsToMany(Group, { through: 'groups_outsourcing_companies', as: 'OutsourcingCompanies' });
const ctx = {};
return this.sequelize.sync({ force: true }).then(() => {
return Group.bulkCreate([
{ name: 'SoccerMoms' },
{ name: 'Coca Cola' },
{ name: 'Dell' },
{ name: 'Pepsi' }
]);
}).then(() => {
return Group.findAll();
}).then(groups => {
ctx.groups = groups;
return groups[0].setOutsourcingCompanies(groups.slice(1));
}).then(() => {
return Group.findOne({
where: {
id: ctx.groups[0].id
},
include: [{ model: Group, as: 'OutsourcingCompanies' }]
});
}).then(group => {
expect(group.OutsourcingCompanies).to.have.length(3);
await this.sequelize.sync({ force: true });
await Group.bulkCreate([
{ name: 'SoccerMoms' },
{ name: 'Coca Cola' },
{ name: 'Dell' },
{ name: 'Pepsi' }
]);
const groups = await Group.findAll();
await groups[0].setOutsourcingCompanies(groups.slice(1));
const group = await Group.findOne({
where: {
id: groups[0].id
},
include: [{ model: Group, as: 'OutsourcingCompanies' }]
});
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', {
dateField: Sequelize.DATE
}, { timestamps: false }),
......@@ -729,29 +713,27 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.belongsToMany(Group, { through: 'group_user' });
Group.belongsToMany(User, { through: 'group_user' });
const ctx = {};
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ dateField: Date.UTC(2014, 1, 20) }),
Group.create({ dateField: Date.UTC(2014, 1, 20) })
]);
}).then(([user, group]) => {
ctx.user = user;
return user.addGroup(group);
}).then(() => {
return User.findOne({
where: {
id: ctx.user.id
},
include: [Group]
});
}).then(user => {
expect(user.dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
expect(user.groups[0].dateField.getTime()).to.equal(Date.UTC(2014, 1, 20));
await this.sequelize.sync({ force: true });
const [user0, group] = await Promise.all([
User.create({ dateField: Date.UTC(2014, 1, 20) }),
Group.create({ dateField: Date.UTC(2014, 1, 20) })
]);
await user0.addGroup(group);
const user = await User.findOne({
where: {
id: user0.id
},
include: [Group]
});
expect(user.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', {
name: DataTypes.STRING
}),
......@@ -773,35 +755,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
as: 'Members'
});
const ctx = {};
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ name: 'Owner' }),
User.create({ name: 'Member' }),
Group.create({ name: 'Group' })
]);
}).then(([owner, member, group]) => {
ctx.owner = owner;
ctx.member = member;
ctx.group = group;
return owner.addGroup(group);
}).then(() => {
return ctx.group.addMember(ctx.member);
}).then(() => {
return ctx.owner.getGroups({
include: [{
model: User,
as: 'Members'
}]
});
}).then(groups => {
expect(groups.length).to.equal(1);
expect(groups[0].Members[0].name).to.equal('Member');
await this.sequelize.sync({ force: true });
const [owner, member, group] = await Promise.all([
User.create({ name: 'Owner' }),
User.create({ name: 'Member' }),
Group.create({ name: 'Group' })
]);
await owner.addGroup(group);
await group.addMember(member);
const groups = await owner.getGroups({
include: [{
model: User,
as: 'Members'
}]
});
expect(groups.length).to.equal(1);
expect(groups[0].Members[0].name).to.equal('Member');
});
});
const createUsersAndItems = function() {
const createUsersAndItems = async function() {
const User = this.sequelize.define('User', {}),
Item = this.sequelize.define('Item', { 'test': DataTypes.STRING });
......@@ -811,46 +788,46 @@ describe(Support.getTestDialectTeaser('Include'), () => {
this.User = User;
this.Item = Item;
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.bulkCreate([{}, {}, {}]).then(() => {
return User.findAll();
}),
Item.bulkCreate([
{ 'test': 'abc' },
{ 'test': 'def' },
{ 'test': 'ghi' }
]).then(() => {
return Item.findAll();
})
]);
}).then(([users, items]) => {
return Promise.all([
users[0].setItem(items[0]),
users[1].setItem(items[1]),
users[2].setItem(items[2])
]);
});
await this.sequelize.sync({ force: true });
const [users, items] = await Promise.all([
User.bulkCreate([{}, {}, {}]).then(() => {
return User.findAll();
}),
Item.bulkCreate([
{ 'test': 'abc' },
{ 'test': 'def' },
{ 'test': 'ghi' }
]).then(() => {
return Item.findAll();
})
]);
return Promise.all([
users[0].setItem(items[0]),
users[1].setItem(items[1]),
users[2].setItem(items[2])
]);
};
describe('where', () => {
beforeEach(function() {
return createUsersAndItems.bind(this)();
beforeEach(async function() {
await createUsersAndItems.bind(this)();
});
it('should support Sequelize.and()', function() {
return this.User.findAll({
it('should support Sequelize.and()', async function() {
const result = await this.User.findAll({
include: [
{ model: this.Item, where: Sequelize.and({ test: 'def' }) }
]
}).then(result => {
expect(result.length).to.eql(1);
expect(result[0].Item.test).to.eql('def');
});
expect(result.length).to.eql(1);
expect(result[0].Item.test).to.eql('def');
});
it('should support Sequelize.or()', function() {
return expect(this.User.findAll({
it('should support Sequelize.or()', async function() {
await expect(this.User.findAll({
include: [
{ model: this.Item, where: Sequelize.or({
test: 'def'
......@@ -863,26 +840,26 @@ describe(Support.getTestDialectTeaser('Include'), () => {
});
describe('findAndCountAll', () => {
it('should include associations to findAndCountAll', function() {
return createUsersAndItems.bind(this)().then(() => {
return this.User.findAndCountAll({
include: [
{ model: this.Item, where: {
test: 'def'
} }
]
});
}).then(result => {
expect(result.count).to.eql(1);
it('should include associations to findAndCountAll', async function() {
await createUsersAndItems.bind(this)();
expect(result.rows.length).to.eql(1);
expect(result.rows[0].Item.test).to.eql('def');
const result = await this.User.findAndCountAll({
include: [
{ model: this.Item, where: {
test: 'def'
} }
]
});
expect(result.count).to.eql(1);
expect(result.rows.length).to.eql(1);
expect(result.rows[0].Item.test).to.eql('def');
});
});
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', {}),
Answer = this.sequelize.define('Answer', {}),
Questionnaire = this.sequelize.define('Questionnaire', {});
......@@ -893,18 +870,17 @@ describe(Support.getTestDialectTeaser('Include'), () => {
Questionnaire.hasMany(Question);
Question.belongsTo(Questionnaire);
return this.sequelize.sync({ force: true }).then(() => {
return Questionnaire.create();
}).then(questionnaire => {
return questionnaire.getQuestions({
include: Answer
});
await this.sequelize.sync({ force: true });
const questionnaire = await Questionnaire.create();
await questionnaire.getQuestions({
include: Answer
});
});
});
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', {
name: DataTypes.STRING
}),
......@@ -915,30 +891,30 @@ describe(Support.getTestDialectTeaser('Include'), () => {
User.hasMany(Group);
Group.belongsTo(User);
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ name: 'User 1' }),
User.create({ name: 'User 2' }),
User.create({ name: 'User 3' }),
Group.create({ name: 'A Group' })
]);
}).then(() => {
return Group.findAll({
include: [{
model: User,
right: true
}]
});
}).then(groups => {
if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(3);
} else {
expect(groups.length).to.equal(1);
}
await this.sequelize.sync({ force: true });
await Promise.all([
User.create({ name: 'User 1' }),
User.create({ name: 'User 2' }),
User.create({ name: 'User 3' }),
Group.create({ name: 'A Group' })
]);
const groups = await Group.findAll({
include: [{
model: User,
right: true
}]
});
if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(3);
} else {
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', {
name: DataTypes.STRING
}),
......@@ -962,47 +938,41 @@ describe(Support.getTestDialectTeaser('Include'), () => {
constraints: false
});
const ctx = {};
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ name: 'Member 1' }),
User.create({ name: 'Member 2' }),
Group.create({ name: 'Group 1' }),
Group.create({ name: 'Group 2' })
]);
}).then(([member1, member2, group1, group2]) => {
ctx.member1 = member1;
ctx.member2 = member2;
ctx.group1 = group1;
ctx.group2 = group2;
}).then(() => {
return Promise.all([
ctx.group1.addMember(ctx.member1),
ctx.group1.addMember(ctx.member2),
ctx.group2.addMember(ctx.member1)
]);
}).then(() => {
return ctx.group2.destroy();
}).then(() => {
return Group.findAll({
include: [{
model: User,
as: 'Members',
right: true
}]
});
}).then(groups => {
if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(2);
} else {
expect(groups.length).to.equal(1);
}
await this.sequelize.sync({ force: true });
const [member1, member2, group1, group2] = await Promise.all([
User.create({ name: 'Member 1' }),
User.create({ name: 'Member 2' }),
Group.create({ name: 'Group 1' }),
Group.create({ name: 'Group 2' })
]);
await Promise.all([
group1.addMember(member1),
group1.addMember(member2),
group2.addMember(member1)
]);
await group2.destroy();
const groups = await Group.findAll({
include: [{
model: User,
as: 'Members',
right: true
}]
});
if (current.dialect.supports['RIGHT JOIN']) {
expect(groups.length).to.equal(2);
} else {
expect(groups.length).to.equal(1);
}
});
});
describe('nested includes', () => {
beforeEach(function() {
beforeEach(async function() {
const Employee = this.sequelize.define('Employee', { 'name': DataTypes.STRING });
const Team = this.sequelize.define('Team', { 'name': DataTypes.STRING });
const Clearence = this.sequelize.define('Clearence', { 'level': DataTypes.INTEGER });
......@@ -1015,29 +985,29 @@ describe(Support.getTestDialectTeaser('Include'), () => {
this.Team = Team;
this.Clearence = Clearence;
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
Team.create({ name: 'TeamA' }),
Team.create({ name: 'TeamB' }),
Employee.create({ name: 'John' }),
Employee.create({ name: 'Jane' }),
Employee.create({ name: 'Josh' }),
Employee.create({ name: 'Jill' }),
Clearence.create({ level: 3 }),
Clearence.create({ level: 5 })
]).then(instances => {
return Promise.all([
instances[0].addMembers([instances[2], instances[3]]),
instances[1].addMembers([instances[4], instances[5]]),
instances[2].setClearence(instances[6]),
instances[3].setClearence(instances[7])
]);
});
});
await this.sequelize.sync({ force: true });
const instances = await Promise.all([
Team.create({ name: 'TeamA' }),
Team.create({ name: 'TeamB' }),
Employee.create({ name: 'John' }),
Employee.create({ name: 'Jane' }),
Employee.create({ name: 'Josh' }),
Employee.create({ name: 'Jill' }),
Clearence.create({ level: 3 }),
Clearence.create({ level: 5 })
]);
await Promise.all([
instances[0].addMembers([instances[2], instances[3]]),
instances[1].addMembers([instances[4], instances[5]]),
instances[2].setClearence(instances[6]),
instances[3].setClearence(instances[7])
]);
});
it('should not ripple grandchild required to top level find when required of child is set to false', function() {
return this.Team.findAll({
it('should not ripple grandchild required to top level find when required of child is set to false', async function() {
const teams = await this.Team.findAll({
include: [
{
association: this.Team.Members,
......@@ -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() {
return this.Team.findOne({
it('should support eager loading associations using the name of the relation (string)', async function() {
const team = await this.Team.findOne({
where: {
name: 'TeamA'
},
......@@ -1066,13 +1036,13 @@ describe(Support.getTestDialectTeaser('Include'), () => {
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() {
return this.Team.findAll({
it('should not ripple grandchild required to top level find when required of child is not given (implicitly false)', async function() {
const teams = await this.Team.findAll({
include: [
{
association: this.Team.Members,
......@@ -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() {
return this.Team.findAll({
it('should ripple grandchild required to top level find when required of child is set to true as well', async function() {
const teams = await this.Team.findAll({
include: [
{
association: this.Team.Members,
......@@ -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'), () => {
this.clock.restore();
});
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('User', {
username: { type: DataTypes.STRING },
uuidv1: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV1 },
......@@ -53,20 +53,18 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
}
});
return this.User.sync({ force: true });
await this.User.sync({ force: true });
});
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"
// 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
return this.User.create({ username: bio }).then(u1 => {
return this.User.findByPk(u1.id).then(u2 => {
expect(u2.username).to.equal(bio);
});
});
const u1 = await this.User.create({ username: bio });
const u2 = await this.User.findByPk(u1.id);
expect(u2.username).to.equal(bio);
});
});
......@@ -77,41 +75,34 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
expect(user.isNewRecord).to.be.ok;
});
it('returns false for saved objects', function() {
return this.User.build({ username: 'user' }).save().then(user => {
expect(user.isNewRecord).to.not.be.ok;
});
it('returns false for saved objects', async function() {
const user = await this.User.build({ username: 'user' }).save();
expect(user.isNewRecord).to.not.be.ok;
});
it('returns false for created objects', function() {
return this.User.create({ username: 'user' }).then(user => {
expect(user.isNewRecord).to.not.be.ok;
});
it('returns false for created objects', async function() {
const user = await this.User.create({ username: 'user' });
expect(user.isNewRecord).to.not.be.ok;
});
it('returns false for objects found by find method', function() {
return this.User.create({ username: 'user' }).then(() => {
return this.User.create({ username: 'user' }).then(user => {
return this.User.findByPk(user.id).then(user => {
expect(user.isNewRecord).to.not.be.ok;
});
});
});
it('returns false for objects found by find method', async function() {
await this.User.create({ username: 'user' });
const user = await this.User.create({ username: 'user' });
const user0 = await this.User.findByPk(user.id);
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 = [];
for (let i = 0; i < 10; i++) {
users[i] = { username: 'user' };
}
return this.User.bulkCreate(users).then(() => {
return this.User.findAll().then(users => {
users.forEach(u => {
expect(u.isNewRecord).to.not.be.ok;
});
});
await this.User.bulkCreate(users);
const users0 = await this.User.findAll();
users0.forEach(u => {
expect(u.isNewRecord).to.not.be.ok;
});
});
});
......@@ -174,260 +165,225 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
});
describe('allowNull date', () => {
it('should be just "null" and not Date with Invalid Date', function() {
return this.User.build({ username: 'a user' }).save().then(() => {
return this.User.findOne({ where: { username: 'a user' } }).then(user => {
expect(user.dateAllowNullTrue).to.be.null;
});
});
it('should be just "null" and not Date with Invalid Date', async function() {
await this.User.build({ username: 'a user' }).save();
const user = await this.User.findOne({ where: { username: 'a user' } });
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();
return this.User.build({ username: 'a user', dateAllowNullTrue: date }).save().then(() => {
return this.User.findOne({ where: { username: 'a user' } }).then(user => {
expect(user.dateAllowNullTrue.toString()).to.equal(date.toString());
});
});
await this.User.build({ username: 'a user', dateAllowNullTrue: date }).save();
const user = await this.User.findOne({ where: { username: 'a user' } });
expect(user.dateAllowNullTrue.toString()).to.equal(date.toString());
});
});
describe('super user boolean', () => {
it('should default to false', function() {
return this.User.build({
it('should default to false', async function() {
await this.User.build({
username: 'a user'
})
.save()
.then(() => {
return this.User.findOne({
where: {
username: 'a user'
}
})
.then(user => {
expect(user.isSuperUser).to.be.false;
});
});
.save();
const user = await this.User.findOne({
where: {
username: 'a user'
}
});
expect(user.isSuperUser).to.be.false;
});
it('should override default when given truthy boolean', function() {
return this.User.build({
it('should override default when given truthy boolean', async function() {
await this.User.build({
username: 'a user',
isSuperUser: true
})
.save()
.then(() => {
return this.User.findOne({
where: {
username: 'a user'
}
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
});
.save();
const user = await this.User.findOne({
where: {
username: 'a user'
}
});
expect(user.isSuperUser).to.be.true;
});
it('should override default when given truthy boolean-string ("true")', function() {
return this.User.build({
it('should override default when given truthy boolean-string ("true")', async function() {
await this.User.build({
username: 'a user',
isSuperUser: 'true'
})
.save()
.then(() => {
return this.User.findOne({
where: {
username: 'a user'
}
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
});
.save();
const user = await this.User.findOne({
where: {
username: 'a user'
}
});
expect(user.isSuperUser).to.be.true;
});
it('should override default when given truthy boolean-int (1)', function() {
return this.User.build({
it('should override default when given truthy boolean-int (1)', async function() {
await this.User.build({
username: 'a user',
isSuperUser: 1
})
.save()
.then(() => {
return this.User.findOne({
where: {
username: 'a user'
}
})
.then(user => {
expect(user.isSuperUser).to.be.true;
});
});
.save();
const user = await this.User.findOne({
where: {
username: 'a user'
}
});
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;
return this.User.build({
username: 'a user',
isSuperUser: 'INCORRECT_VALUE_TYPE'
})
.save()
.then(() => {
callCount += 1;
try {
await this.User.build({
username: 'a user',
isSuperUser: 'INCORRECT_VALUE_TYPE'
})
.catch(err => {
expect(callCount).to.equal(0);
expect(err).to.exist;
expect(err.message).to.exist;
});
.save();
callCount += 1;
} catch (err) {
expect(callCount).to.equal(0);
expect(err).to.exist;
expect(err.message).to.exist;
}
});
});
});
describe('complete', () => {
it('gets triggered if an error occurs', function() {
return this.User.findOne({ where: ['asdasdasd'] }).catch(err => {
it('gets triggered if an error occurs', async function() {
try {
await this.User.findOne({ where: ['asdasdasd'] });
} catch (err) {
expect(err).to.exist;
expect(err.message).to.exist;
});
}
});
it('gets triggered if everything was ok', function() {
return this.User.count().then(result => {
expect(result).to.exist;
});
it('gets triggered if everything was ok', async function() {
const result = await this.User.count();
expect(result).to.exist;
});
});
describe('findAll', () => {
beforeEach(function() {
beforeEach(async function() {
this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, { paranoid: true });
this.ParanoidUser.hasOne(this.ParanoidUser);
return this.ParanoidUser.sync({ force: true });
await this.ParanoidUser.sync({ force: true });
});
it('sql should have paranoid condition', function() {
return this.ParanoidUser.create({ username: 'cuss' })
.then(() => {
return this.ParanoidUser.findAll();
})
.then(users => {
expect(users).to.have.length(1);
return users[0].destroy();
})
.then(() => {
return this.ParanoidUser.findAll();
})
.then(users => {
expect(users).to.have.length(0);
});
it('sql should have paranoid condition', async function() {
await this.ParanoidUser.create({ username: 'cuss' });
const users0 = await this.ParanoidUser.findAll();
expect(users0).to.have.length(1);
await users0[0].destroy();
const users = await this.ParanoidUser.findAll();
expect(users).to.have.length(0);
});
it('sequelize.and as where should include paranoid condition', function() {
return this.ParanoidUser.create({ username: 'cuss' })
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.and({
username: 'cuss'
})
});
})
.then(users => {
expect(users).to.have.length(1);
return users[0].destroy();
it('sequelize.and as where should include paranoid condition', async function() {
await this.ParanoidUser.create({ username: 'cuss' });
const users0 = await this.ParanoidUser.findAll({
where: this.sequelize.and({
username: 'cuss'
})
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.and({
username: 'cuss'
})
});
});
expect(users0).to.have.length(1);
await users0[0].destroy();
const users = await this.ParanoidUser.findAll({
where: this.sequelize.and({
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() {
return this.ParanoidUser.create({ username: 'cuss' })
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.or({
username: 'cuss'
})
});
})
.then(users => {
expect(users).to.have.length(1);
return users[0].destroy();
it('sequelize.or as where should include paranoid condition', async function() {
await this.ParanoidUser.create({ username: 'cuss' });
const users0 = await this.ParanoidUser.findAll({
where: this.sequelize.or({
username: 'cuss'
})
.then(() => {
return this.ParanoidUser.findAll({
where: this.sequelize.or({
username: 'cuss'
})
});
});
expect(users0).to.have.length(1);
await users0[0].destroy();
const users = await this.ParanoidUser.findAll({
where: this.sequelize.or({
username: 'cuss'
})
.then(users => {
expect(users).to.have.length(0);
});
});
});
it('escapes a single single quotes properly in where clauses', function() {
return this.User
.create({ username: "user'name" })
.then(() => {
return this.User.findAll({
where: { username: "user'name" }
}).then(users => {
expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user'name");
});
});
expect(users).to.have.length(0);
});
it('escapes two single quotes properly in where clauses', function() {
return this.User
.create({ username: "user''name" })
.then(() => {
return this.User.findAll({
where: { username: "user''name" }
}).then(users => {
expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user''name");
});
});
});
it('escapes a single single quotes properly in where clauses', async function() {
await this.User
.create({ username: "user'name" });
it('returns the timestamps if no attributes have been specified', function() {
return this.User.create({ username: 'fnord' }).then(() => {
return this.User.findAll().then(users => {
expect(users[0].createdAt).to.exist;
});
const users = await this.User.findAll({
where: { username: "user'name" }
});
expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user'name");
});
it('does not return the timestamps if the username attribute has been specified', function() {
return this.User.create({ username: 'fnord' }).then(() => {
return this.User.findAll({ attributes: ['username'] }).then(users => {
expect(users[0].createdAt).not.to.exist;
expect(users[0].username).to.exist;
});
it('escapes two single quotes properly in where clauses', async function() {
await this.User
.create({ username: "user''name" });
const users = await this.User.findAll({
where: { username: "user''name" }
});
expect(users.length).to.equal(1);
expect(users[0].username).to.equal("user''name");
});
it('creates the deletedAt property, when defining paranoid as true', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
expect(users[0].deletedAt).to.be.null;
});
});
it('returns the timestamps if no attributes have been specified', async function() {
await this.User.create({ username: 'fnord' });
const users = await this.User.findAll();
expect(users[0].createdAt).to.exist;
});
it('destroys a record with a primary key of something other than id', function() {
it('does not return the timestamps if the username attribute has been specified', async function() {
await this.User.create({ username: 'fnord' });
const users = await this.User.findAll({ attributes: ['username'] });
expect(users[0].createdAt).not.to.exist;
expect(users[0].username).to.exist;
});
it('creates the deletedAt property, when defining paranoid as true', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
expect(users[0].deletedAt).to.be.null;
});
it('destroys a record with a primary key of something other than id', async function() {
const UserDestroy = this.sequelize.define('UserDestroy', {
newId: {
type: DataTypes.STRING,
......@@ -436,77 +392,58 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
email: DataTypes.STRING
});
return UserDestroy.sync().then(() => {
return UserDestroy.create({ newId: '123ABC', email: 'hello' }).then(() => {
return UserDestroy.findOne({ where: { email: 'hello' } }).then(user => {
return user.destroy();
});
});
});
await UserDestroy.sync();
await UserDestroy.create({ newId: '123ABC', email: 'hello' });
const user = await UserDestroy.findOne({ where: { email: 'hello' } });
await user.destroy();
});
it('sets deletedAt property to a specific date when deleting an instance', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
return users[0].destroy().then(() => {
expect(users[0].deletedAt.getMonth).to.exist;
it('sets deletedAt property to a specific date when deleting an instance', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
await users[0].destroy();
expect(users[0].deletedAt.getMonth).to.exist;
return users[0].reload({ paranoid: false }).then(user => {
expect(user.deletedAt.getMonth).to.exist;
});
});
});
});
const user = await users[0].reload({ paranoid: false });
expect(user.deletedAt.getMonth).to.exist;
});
it('keeps the deletedAt-attribute with value null, when running update', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
return users[0].update({ username: 'newFnord' }).then(user => {
expect(user.deletedAt).not.to.exist;
});
});
});
it('keeps the deletedAt-attribute with value null, when running update', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
const user = await users[0].update({ username: 'newFnord' });
expect(user.deletedAt).not.to.exist;
});
it('keeps the deletedAt-attribute with value null, when updating associations', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
return this.ParanoidUser.create({ username: 'linkedFnord' }).then(linkedUser => {
return users[0].setParanoidUser(linkedUser).then(user => {
expect(user.deletedAt).not.to.exist;
});
});
});
});
it('keeps the deletedAt-attribute with value null, when updating associations', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
const linkedUser = await this.ParanoidUser.create({ username: 'linkedFnord' });
const user = await users[0].setParanoidUser(linkedUser);
expect(user.deletedAt).not.to.exist;
});
it('can reuse query option objects', function() {
return this.User.create({ username: 'fnord' }).then(() => {
const query = { where: { username: 'fnord' } };
return this.User.findAll(query).then(users => {
expect(users[0].username).to.equal('fnord');
return this.User.findAll(query).then(users => {
expect(users[0].username).to.equal('fnord');
});
});
});
it('can reuse query option objects', async function() {
await this.User.create({ username: 'fnord' });
const query = { where: { username: 'fnord' } };
const users = await this.User.findAll(query);
expect(users[0].username).to.equal('fnord');
const users0 = await this.User.findAll(query);
expect(users0[0].username).to.equal('fnord');
});
});
describe('findOne', () => {
it('can reuse query option objects', function() {
return this.User.create({ username: 'fnord' }).then(() => {
const query = { where: { username: 'fnord' } };
return this.User.findOne(query).then(user => {
expect(user.username).to.equal('fnord');
return this.User.findOne(query).then(user => {
expect(user.username).to.equal('fnord');
});
});
});
});
it('returns null for null, undefined, and unset boolean values', function() {
it('can reuse query option objects', async function() {
await this.User.create({ username: 'fnord' });
const query = { where: { username: 'fnord' } };
const user = await this.User.findOne(query);
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() {
const Setting = this.sequelize.define('SettingHelper', {
setting_key: DataTypes.STRING,
bool_value: { type: DataTypes.BOOLEAN, allowNull: true },
......@@ -514,28 +451,23 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
bool_value3: { type: DataTypes.BOOLEAN, allowNull: true }
}, { timestamps: false, logging: false });
return Setting.sync({ force: true }).then(() => {
return Setting.create({ setting_key: 'test', bool_value: null, bool_value2: undefined }).then(() => {
return Setting.findOne({ where: { setting_key: 'test' } }).then(setting => {
expect(setting.bool_value).to.equal(null);
expect(setting.bool_value2).to.equal(null);
expect(setting.bool_value3).to.equal(null);
});
});
});
await Setting.sync({ force: true });
await Setting.create({ setting_key: 'test', bool_value: null, bool_value2: undefined });
const setting = await Setting.findOne({ where: { setting_key: 'test' } });
expect(setting.bool_value).to.equal(null);
expect(setting.bool_value2).to.equal(null);
expect(setting.bool_value3).to.equal(null);
});
});
describe('equals', () => {
it('can compare records with Date field', function() {
return this.User.create({ username: 'fnord' }).then(user1 => {
return this.User.findOne({ where: { username: 'fnord' } }).then(user2 => {
expect(user1.equals(user2)).to.be.true;
});
});
it('can compare records with Date field', async function() {
const user1 = await this.User.create({ username: 'fnord' });
const user2 = await this.User.findOne({ where: { username: 'fnord' } });
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', {
username: DataTypes.STRING,
age: DataTypes.INTEGER
......@@ -549,80 +481,65 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
this.UserAssociationEqual.hasMany(this.ProjectAssociationEqual, { as: 'Projects', foreignKey: 'userId' });
this.ProjectAssociationEqual.belongsTo(this.UserAssociationEqual, { as: 'Users', foreignKey: 'userId' });
return this.UserAssociationEqual.sync({ force: true }).then(() => {
return this.ProjectAssociationEqual.sync({ force: true }).then(() => {
return this.UserAssociationEqual.create({ username: 'jimhalpert' }).then(user1 => {
return this.ProjectAssociationEqual.create({ title: 'A Cool Project' }).then(project1 => {
return user1.setProjects([project1]).then(() => {
return this.UserAssociationEqual.findOne({ where: { username: 'jimhalpert' }, include: [{ model: this.ProjectAssociationEqual, as: 'Projects' }] }).then(user2 => {
return this.UserAssociationEqual.create({ username: 'pambeesly' }).then(user3 => {
expect(user1.get('Projects')).to.not.exist;
expect(user2.get('Projects')).to.exist;
expect(user1.equals(user2)).to.be.true;
expect(user2.equals(user1)).to.be.true;
expect(user1.equals(user3)).to.not.be.true;
expect(user3.equals(user1)).to.not.be.true;
});
});
});
});
});
});
});
await this.UserAssociationEqual.sync({ force: true });
await this.ProjectAssociationEqual.sync({ force: true });
const user1 = await this.UserAssociationEqual.create({ username: 'jimhalpert' });
const project1 = await this.ProjectAssociationEqual.create({ title: 'A Cool Project' });
await user1.setProjects([project1]);
const user2 = await this.UserAssociationEqual.findOne({ where: { username: 'jimhalpert' }, include: [{ model: this.ProjectAssociationEqual, as: 'Projects' }] });
const user3 = await this.UserAssociationEqual.create({ username: 'pambeesly' });
expect(user1.get('Projects')).to.not.exist;
expect(user2.get('Projects')).to.exist;
expect(user1.equals(user2)).to.be.true;
expect(user2.equals(user1)).to.be.true;
expect(user1.equals(user3)).to.not.be.true;
expect(user3.equals(user1)).to.not.be.true;
});
});
describe('values', () => {
it('returns all values', function() {
it('returns all values', async function() {
const User = this.sequelize.define('UserHelper', {
username: DataTypes.STRING
}, { timestamps: false, logging: false });
return User.sync().then(() => {
const user = User.build({ username: 'foo' });
expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null });
});
await User.sync();
const user = User.build({ username: 'foo' });
expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null });
});
});
describe('isSoftDeleted', () => {
beforeEach(function() {
beforeEach(async function() {
this.ParanoidUser = this.sequelize.define('ParanoidUser', {
username: { type: DataTypes.STRING }
}, { paranoid: true });
return this.ParanoidUser.sync({ force: true });
await this.ParanoidUser.sync({ force: true });
});
it('should return false when model is just created', function() {
return this.ParanoidUser.create({ username: 'foo' }).then(user => {
expect(user.isSoftDeleted()).to.be.false;
});
it('should return false when model is just created', async function() {
const user = await this.ParanoidUser.create({ username: 'foo' });
expect(user.isSoftDeleted()).to.be.false;
});
it('returns false if user is not soft deleted', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
expect(users[0].isSoftDeleted()).to.be.false;
});
});
it('returns false if user is not soft deleted', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
expect(users[0].isSoftDeleted()).to.be.false;
});
it('returns true if user is soft deleted', function() {
return this.ParanoidUser.create({ username: 'fnord' }).then(() => {
return this.ParanoidUser.findAll().then(users => {
return users[0].destroy().then(() => {
expect(users[0].isSoftDeleted()).to.be.true;
it('returns true if user is soft deleted', async function() {
await this.ParanoidUser.create({ username: 'fnord' });
const users = await this.ParanoidUser.findAll();
await users[0].destroy();
expect(users[0].isSoftDeleted()).to.be.true;
return users[0].reload({ paranoid: false }).then(user => {
expect(user.isSoftDeleted()).to.be.true;
});
});
});
});
const user = await users[0].reload({ paranoid: false });
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', {
username: { type: DataTypes.STRING }
}, {
......@@ -632,21 +549,16 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
this.ParanoidUserWithCustomDeletedAt.hasOne(this.ParanoidUser);
return this.ParanoidUserWithCustomDeletedAt.sync({ force: true }).then(() => {
return this.ParanoidUserWithCustomDeletedAt.create({ username: 'fnord' }).then(() => {
return this.ParanoidUserWithCustomDeletedAt.findAll().then(users => {
expect(users[0].isSoftDeleted()).to.be.false;
await this.ParanoidUserWithCustomDeletedAt.sync({ force: true });
await this.ParanoidUserWithCustomDeletedAt.create({ username: 'fnord' });
const users = await this.ParanoidUserWithCustomDeletedAt.findAll();
expect(users[0].isSoftDeleted()).to.be.false;
return users[0].destroy().then(() => {
expect(users[0].isSoftDeleted()).to.be.true;
await users[0].destroy();
expect(users[0].isSoftDeleted()).to.be.true;
return users[0].reload({ paranoid: false }).then(user => {
expect(user.isSoftDeleted()).to.be.true;
});
});
});
});
});
const user = await users[0].reload({ paranoid: false });
expect(user.isSoftDeleted()).to.be.true;
});
});
......@@ -656,7 +568,7 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
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', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
......@@ -669,124 +581,106 @@ describe(Support.getTestDialectTeaser('Instance'), () => {
{ username: 'Paul', secretValue: '43' },
{ username: 'Bob', secretValue: '44' }];
return ParanoidUser.sync({ force: true }).then(() => {
return ParanoidUser.bulkCreate(data);
}).then(() => {
return ParanoidUser.findOne({ where: { secretValue: '42' } });
}).then(user => {
return user.destroy().then(() => {
return user.restore();
});
}).then(() => {
return ParanoidUser.findOne({ where: { secretValue: '42' } });
}).then(user => {
expect(user).to.be.ok;
expect(user.username).to.equal('Peter');
});
await ParanoidUser.sync({ force: true });
await ParanoidUser.bulkCreate(data);
const user0 = await ParanoidUser.findOne({ where: { secretValue: '42' } });
await user0.destroy();
await user0.restore();
const user = await ParanoidUser.findOne({ where: { secretValue: '42' } });
expect(user).to.be.ok;
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', {
username: DataTypes.STRING,
destroyTime: DataTypes.DATE
}, { paranoid: true, deletedAt: 'destroyTime' });
return ParanoidUser.sync({ force: true }).then(() => {
return ParanoidUser.create({
username: 'username'
});
}).then(user => {
return user.destroy();
}).then(user => {
expect(user.destroyTime).to.be.ok;
expect(user.deletedAt).to.not.be.ok;
return user.restore();
}).then(user => {
expect(user.destroyTime).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok;
expect(user.destroyTime).to.not.be.ok;
expect(user.deletedAt).to.not.be.ok;
await ParanoidUser.sync({ force: true });
const user2 = await ParanoidUser.create({
username: 'username'
});
const user1 = await user2.destroy();
expect(user1.destroyTime).to.be.ok;
expect(user1.deletedAt).to.not.be.ok;
const user0 = await user1.restore();
expect(user0.destroyTime).to.not.be.ok;
const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user).to.be.ok;
expect(user.destroyTime).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', {
username: DataTypes.STRING,
deletedAt: { type: DataTypes.DATE, field: 'deleted_at' }
}, { paranoid: true });
return ParanoidUser.sync({ force: true }).then(() => {
return ParanoidUser.create({
username: 'username'
});
}).then(user => {
return user.destroy();
}).then(user => {
expect(user.dataValues.deletedAt).to.be.ok;
expect(user.dataValues.deleted_at).to.not.be.ok;
return user.restore();
}).then(user => {
expect(user.dataValues.deletedAt).to.not.be.ok;
expect(user.dataValues.deleted_at).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok;
expect(user.deletedAt).to.not.be.ok;
expect(user.deleted_at).to.not.be.ok;
await ParanoidUser.sync({ force: true });
const user2 = await ParanoidUser.create({
username: 'username'
});
const user1 = await user2.destroy();
expect(user1.dataValues.deletedAt).to.be.ok;
expect(user1.dataValues.deleted_at).to.not.be.ok;
const user0 = await user1.restore();
expect(user0.dataValues.deletedAt).to.not.be.ok;
expect(user0.dataValues.deleted_at).to.not.be.ok;
const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user).to.be.ok;
expect(user.deletedAt).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', {
username: DataTypes.STRING,
destroyTime: { type: DataTypes.DATE, field: 'destroy_time' }
}, { paranoid: true, deletedAt: 'destroyTime' });
return ParanoidUser.sync({ force: true }).then(() => {
return ParanoidUser.create({
username: 'username'
});
}).then(user => {
return user.destroy();
}).then(user => {
expect(user.dataValues.destroyTime).to.be.ok;
expect(user.dataValues.deletedAt).to.not.be.ok;
expect(user.dataValues.destroy_time).to.not.be.ok;
return user.restore();
}).then(user => {
expect(user.dataValues.destroyTime).to.not.be.ok;
expect(user.dataValues.destroy_time).to.not.be.ok;
return ParanoidUser.findOne({ where: { username: 'username' } });
}).then(user => {
expect(user).to.be.ok;
expect(user.destroyTime).to.not.be.ok;
expect(user.destroy_time).to.not.be.ok;
await ParanoidUser.sync({ force: true });
const user2 = await ParanoidUser.create({
username: 'username'
});
const user1 = await user2.destroy();
expect(user1.dataValues.destroyTime).to.be.ok;
expect(user1.dataValues.deletedAt).to.not.be.ok;
expect(user1.dataValues.destroy_time).to.not.be.ok;
const user0 = await user1.restore();
expect(user0.dataValues.destroyTime).to.not.be.ok;
expect(user0.dataValues.destroy_time).to.not.be.ok;
const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user).to.be.ok;
expect(user.destroyTime).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', {
username: DataTypes.STRING,
deletedAt: { type: DataTypes.DATE, defaultValue: new Date(0) }
}, { paranoid: true });
return ParanoidUser.sync({ force: true }).then(() => {
return ParanoidUser.create({
username: 'username'
});
}).then(user => {
return user.destroy();
}).then(user => {
return user.restore();
}).then(user => {
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.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
await ParanoidUser.sync({ force: true });
const user2 = await ParanoidUser.create({
username: 'username'
});
const user1 = await user2.destroy();
const user0 = await user1.restore();
expect(user0.dataValues.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
const user = await ParanoidUser.findOne({ where: { username: 'username' } });
expect(user).to.be.ok;
expect(user.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
});
});
});
......@@ -8,7 +8,7 @@ const chai = require('chai'),
describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
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', {
username: Sequelize.STRING,
email: {
......@@ -22,20 +22,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return User.sync({ force: true }).then(() => {
return User.create({ username: 'bob', email: 'hello@world.com' }).then(user => {
return User
.update({ username: 'toni' }, { where: { id: user.id } })
.then(() => {
return User.findByPk(1).then(user => {
expect(user.username).to.equal('toni');
});
});
});
});
await User.sync({ force: true });
const user = await User.create({ username: 'bob', email: 'hello@world.com' });
await User
.update({ username: 'toni' }, { where: { id: user.id } });
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', {
name: {
type: Sequelize.STRING,
......@@ -46,17 +43,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return Model.sync({ force: true }).then(() => {
return Model.create({ name: 'World' }).then(model => {
return model.update({ name: '' }).catch(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed');
});
});
});
await Model.sync({ force: true });
const model = await Model.create({ name: 'World' });
try {
await model.update({ name: '' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
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', {
name: {
type: Sequelize.STRING,
......@@ -67,17 +65,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return Model.sync({ force: true }).then(() => {
return Model.create({ name: 'World' }).then(() => {
return Model.update({ name: '' }, { where: { id: 1 } }).catch(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.get('name')[0].message).to.equal('Validation notEmpty on name failed');
});
});
});
await Model.sync({ force: true });
await Model.create({ name: 'World' });
try {
await Model.update({ name: '' }, { where: { id: 1 } });
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
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', {
uniqueName: { type: Sequelize.STRING, unique: 'uniqueName' }
});
......@@ -85,24 +84,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName: 'unique name one' },
{ uniqueName: 'unique name two' }
];
return Model.sync({ force: true })
.then(() => {
return Model.create(records[0]);
}).then(instance => {
expect(instance).to.be.ok;
return Model.create(records[1]);
}).then(instance => {
expect(instance).to.be.ok;
return expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.include('must be unique');
});
});
it('should allow a custom unique constraint error message', function() {
await Model.sync({ force: true });
const instance0 = await Model.create(records[0]);
expect(instance0).to.be.ok;
const instance = await Model.create(records[1]);
expect(instance).to.be.ok;
const err = await expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.include('must be unique');
});
it('should allow a custom unique constraint error message', async function() {
const Model = this.sequelize.define('model', {
uniqueName: {
type: Sequelize.STRING,
......@@ -113,24 +107,19 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName: 'unique name one' },
{ uniqueName: 'unique name two' }
];
return Model.sync({ force: true })
.then(() => {
return Model.create(records[0]);
}).then(instance => {
expect(instance).to.be.ok;
return Model.create(records[1]);
}).then(instance => {
expect(instance).to.be.ok;
return expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.equal('custom unique error message');
});
});
it('should handle multiple unique messages correctly', function() {
await Model.sync({ force: true });
const instance0 = await Model.create(records[0]);
expect(instance0).to.be.ok;
const instance = await Model.create(records[1]);
expect(instance).to.be.ok;
const err = await expect(Model.update(records[0], { where: { id: instance.id } })).to.be.rejected;
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName');
expect(err.errors[0].message).to.equal('custom unique error message');
});
it('should handle multiple unique messages correctly', async function() {
const Model = this.sequelize.define('model', {
uniqueName1: {
type: Sequelize.STRING,
......@@ -146,31 +135,26 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
{ uniqueName1: 'unique name one', uniqueName2: 'this is ok' },
{ uniqueName1: 'this is ok', uniqueName2: 'unique name one' }
];
return Model.sync({ force: true })
.then(() => {
return Model.create(records[0]);
}).then(instance => {
expect(instance).to.be.ok;
return expect(Model.create(records[1])).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName1');
expect(err.errors[0].message).to.equal('custom unique error message 1');
return expect(Model.create(records[2])).to.be.rejected;
}).then(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName2');
expect(err.errors[0].message).to.equal('custom unique error message 2');
});
await Model.sync({ force: true });
const instance = await Model.create(records[0]);
expect(instance).to.be.ok;
const err0 = await expect(Model.create(records[1])).to.be.rejected;
expect(err0).to.be.an.instanceOf(Error);
expect(err0.errors).to.have.length(1);
expect(err0.errors[0].path).to.include('uniqueName1');
expect(err0.errors[0].message).to.equal('custom unique error message 1');
const err = await expect(Model.create(records[2])).to.be.rejected;
expect(err).to.be.an.instanceOf(Error);
expect(err.errors).to.have.length(1);
expect(err.errors[0].path).to.include('uniqueName2');
expect(err.errors[0].message).to.equal('custom unique error message 2');
});
});
describe('#create', () => {
describe('generic', () => {
beforeEach(function() {
beforeEach(async function() {
const Project = this.sequelize.define('Project', {
name: {
type: Sequelize.STRING,
......@@ -189,34 +173,31 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
Project.hasOne(Task);
Task.belongsTo(Project);
return this.sequelize.sync({ force: true }).then(() => {
this.Project = Project;
this.Task = Task;
});
await this.sequelize.sync({ force: true });
this.Project = Project;
this.Task = Task;
});
it('correctly throws an error using create method ', function() {
return this.Project.create({ name: 'nope' }).catch(err => {
it('correctly throws an error using create method ', async function() {
try {
await this.Project.create({ name: 'nope' });
} catch (err) {
expect(err).to.have.ownProperty('name');
});
}
});
it('correctly validates using create method ', function() {
return this.Project.create({}).then(project => {
return this.Task.create({ something: 1 }).then(task => {
return project.setTask(task).then(task => {
expect(task.ProjectId).to.not.be.null;
return task.setProject(project).then(project => {
expect(project.ProjectId).to.not.be.null;
});
});
});
});
it('correctly validates using create method ', async function() {
const project = await this.Project.create({});
const task = await this.Task.create({ something: 1 });
const task0 = await project.setTask(task);
expect(task0.ProjectId).to.not.be.null;
const project0 = await task0.setProject(project);
expect(project0.ProjectId).to.not.be.null;
});
});
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', {
id: {
type: Sequelize.INTEGER,
......@@ -228,15 +209,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return User.sync({ force: true }).then(() => {
return User.create({ id: 'helloworld' }).catch(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.get('id')[0].message).to.equal('Validation isInt on id failed');
});
});
await User.sync({ force: true });
try {
await User.create({ id: 'helloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
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', {
username: {
type: Sequelize.INTEGER,
......@@ -248,16 +231,18 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return User.sync({ force: true }).then(() => {
return User.create({ username: 'helloworldhelloworld' }).catch(err => {
expect(err).to.be.an.instanceOf(Error);
expect(err.get('username')[0].message).to.equal('Username must be an integer!');
});
});
await User.sync({ force: true });
try {
await User.create({ username: 'helloworldhelloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
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', () => {
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('UserId', {
id: {
type: Sequelize.INTEGER,
......@@ -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() {
return this.User.create({ id: 'helloworld' }).catch(err => {
it('should emit an error when we try to enter in a string for the id key with validation arguments', async function() {
try {
await this.User.create({ id: 'helloworld' });
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
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' });
return expect(user.validate()).to.be.rejected.then(err => {
expect(err.get('id')[0].message).to.equal('ID must be an integer!');
});
const err = await expect(user.validate()).to.be.rejected;
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' });
return user.save().catch(err => {
try {
await user.save();
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
expect(err.get('id')[0].message).to.equal('ID must be an integer!');
});
}
});
});
});
describe('pass all paths when validating', () => {
beforeEach(function() {
beforeEach(async function() {
const Project = this.sequelize.define('Project', {
name: {
type: Sequelize.STRING,
......@@ -325,25 +314,25 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
Project.hasOne(Task);
Task.belongsTo(Project);
return Project.sync({ force: true }).then(() => {
return Task.sync({ force: true }).then(() => {
this.Project = Project;
this.Task = Task;
});
});
await Project.sync({ force: true });
await Task.sync({ force: true });
this.Project = Project;
this.Task = Task;
});
it('produce 3 errors', function() {
return this.Project.create({}).catch(err => {
it('produce 3 errors', async function() {
try {
await this.Project.create({});
} catch (err) {
expect(err).to.be.an.instanceOf(Error);
delete err.stack; // longStackTraces
expect(err.errors).to.have.length(3);
});
}
});
});
describe('not null schema validation', () => {
beforeEach(function() {
beforeEach(async function() {
const Project = this.sequelize.define('Project', {
name: {
type: Sequelize.STRING,
......@@ -354,13 +343,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return this.sequelize.sync({ force: true }).then(() => {
this.Project = Project;
});
await this.sequelize.sync({ force: true });
this.Project = Project;
});
it('correctly throws an error using create method ', function() {
return this.Project.create({})
it('correctly throws an error using create method ', async function() {
await this.Project.create({})
.then(() => {
throw new Error('Validation must be failed');
}, () => {
......@@ -368,18 +356,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
});
});
it('correctly throws an error using create method with default generated messages', function() {
return this.Project.create({}).catch(err => {
it('correctly throws an error using create method with default generated messages', async function() {
try {
await this.Project.create({});
} catch (err) {
expect(err).to.have.property('name', 'SequelizeValidationError');
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[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()}`, {
name: {
type: Sequelize.STRING,
......@@ -397,43 +387,39 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingUser = User.build({ name: '3' });
return expect(failingUser.validate()).to.be.rejected.then(error => {
expect(error).to.be.an.instanceOf(Error);
expect(error.get('name')[0].message).to.equal("name should equal '2'");
const error = await expect(failingUser.validate()).to.be.rejected;
expect(error).to.be.an.instanceOf(Error);
expect(error.get('name')[0].message).to.equal("name should equal '2'");
const successfulUser = User.build({ name: '2' });
return expect(successfulUser.validate()).not.to.be.rejected;
});
const successfulUser = User.build({ name: '2' });
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()}`, {
name: {
type: Sequelize.STRING,
validate: {
customFn(val) {
return User.findAll()
.then(() => {
if (val === 'error') {
throw new Error('Invalid username');
}
});
async customFn(val) {
await User.findAll();
if (val === 'error') {
throw new Error('Invalid username');
}
}
}
}
});
return User.sync().then(() => {
return expect(User.build({ name: 'error' }).validate()).to.be.rejected.then(error => {
expect(error).to.be.instanceof(Sequelize.ValidationError);
expect(error.get('name')[0].message).to.equal('Invalid username');
await User.sync();
const error = await expect(User.build({ name: 'error' }).validate()).to.be.rejected;
expect(error).to.be.instanceof(Sequelize.ValidationError);
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()}`, {
age: {
type: Sequelize.INTEGER,
......@@ -444,16 +430,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return expect(User
const error = await expect(User
.build({ age: -1 })
.validate())
.to.be.rejected
.then(error => {
expect(error.get('age')[0].message).to.equal('must be positive');
});
.to.be.rejected;
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()}`, {
field1: {
type: Sequelize.INTEGER,
......@@ -473,21 +458,20 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return expect(Foo
const error = await expect(Foo
.build({ field1: null, field2: null })
.validate())
.to.be.rejected
.then(error => {
expect(error.get('xnor')[0].message).to.equal('xnor failed');
return expect(Foo
.build({ field1: 33, field2: null })
.validate())
.not.to.be.rejected;
});
.to.be.rejected;
expect(error.get('xnor')[0].message).to.equal('xnor failed');
await expect(Foo
.build({ field1: 33, field2: null })
.validate())
.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()}`, {
bar: {
type: Sequelize.STRING,
......@@ -497,12 +481,11 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
}),
foo = Foo.build({ bar: 'a' });
return expect(foo.validate()).not.to.be.rejected.then(() => {
return expect(foo.validate()).not.to.be.rejected;
});
await 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 Bar = this.sequelize.define(`Bar${config.rand()}`, {
......@@ -517,13 +500,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
const failingBar = Bar.build({ field: 'value3' });
return expect(failingBar.validate()).to.be.rejected.then(errors => {
expect(errors.get('field')).to.have.length(1);
expect(errors.get('field')[0].message).to.equal('Validation isIn on field failed');
});
const errors = await expect(failingBar.validate()).to.be.rejected;
expect(errors.get('field')).to.have.length(1);
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 Bar = this.sequelize.define(`Bar${config.rand()}`, {
......@@ -538,10 +520,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
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 Bar = this.sequelize.define(`Bar${config.rand()}`, {
......@@ -556,10 +538,10 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
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', {
name: {
type: Sequelize.STRING,
......@@ -569,18 +551,15 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return User.sync({ force: true }).then(() => {
return User.create({ name: 'RedCat' }).then(user => {
expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat');
return expect(user.save()).to.be.rejected.then(errors => {
expect(errors.get('name')[0].message).to.eql('Validation isImmutable on name failed');
});
});
});
await User.sync({ force: true });
const user = await User.create({ name: 'RedCat' });
expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat');
const errors = await expect(user.save()).to.be.rejected;
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', {
name: {
type: Sequelize.STRING,
......@@ -594,94 +573,94 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
expect(user.getDataValue('name')).to.equal('RedCat');
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', {
'email': {
type: Sequelize.STRING
}
});
return expect(User.build({
await expect(User.build({
email: ['iama', 'dummy.com']
}).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', {
'email': {
type: Sequelize.STRING(20)
}
});
return expect(User.build({
await expect(User.build({
email: ['iama', 'dummy.com']
}).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', {
'email': {
type: Sequelize.TEXT
}
});
return expect(User.build({
await expect(User.build({
email: ['iama', 'dummy.com']
}).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', {
'email': {
type: Sequelize.STRING
}
});
return expect(User.build({
await expect(User.build({
email: { lol: true }
}).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', {
'email': {
type: Sequelize.STRING(20)
}
});
return expect(User.build({
await expect(User.build({
email: { lol: true }
}).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', {
'email': {
type: Sequelize.TEXT
}
});
return expect(User.build({
await expect(User.build({
email: { lol: true }
}).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', {
'email': {
type: Sequelize.STRING
}
});
return expect(User.build({
await expect(User.build({
email: null
}).validate()).not.to.be.rejected;
});
it('validates VIRTUAL fields', function() {
it('validates VIRTUAL fields', async function() {
const User = this.sequelize.define('user', {
password_hash: Sequelize.STRING,
salt: Sequelize.STRING,
......@@ -701,7 +680,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return Promise.all([
await Promise.all([
expect(User.build({
password: 'short',
salt: '42'
......@@ -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 => {
return val.length === 7;
});
......@@ -729,14 +708,14 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
}
});
return expect(User.build({
await expect(User.build({
name: 'abcdefg'
}).validate()).not.to.be.rejected.then(() => {
return expect(User.build({
name: 'a'
}).validate()).to.be.rejected;
}).then(errors => {
expect(errors.get('name')[0].message).to.equal('Validation isExactly7Characters on name failed');
});
}).validate()).not.to.be.rejected;
const errors = await expect(User.build({
name: 'a'
}).validate()).to.be.rejected;
expect(errors.get('name')[0].message).to.equal('Validation isExactly7Characters on name failed');
});
});
......@@ -11,7 +11,7 @@ const chai = require('chai'),
describe('model', () => {
if (current.dialect.supports.JSON) {
describe('json', () => {
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
emergency_contact: DataTypes.JSON,
......@@ -20,21 +20,19 @@ describe('model', () => {
this.Order = this.sequelize.define('Order');
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() {
return this.sequelize.queryInterface.describeTable('Users')
.then(table => {
// expected for mariadb 10.4 : https://jira.mariadb.org/browse/MDEV-15558
if (dialect !== 'mariadb') {
expect(table.emergency_contact.type).to.equal('JSON');
}
});
it('should tell me that a column is json', async function() {
const table = await this.sequelize.queryInterface.describeTable('Users');
// expected for mariadb 10.4 : https://jira.mariadb.org/browse/MDEV-15558
if (dialect !== 'mariadb') {
expect(table.emergency_contact.type).to.equal('JSON');
}
});
it('should use a placeholder for json with insert', function() {
return this.User.create({
it('should use a placeholder for json with insert', async function() {
await this.User.create({
username: 'bob',
emergency_contact: { name: 'joe', phones: [1337, 42] }
}, {
......@@ -49,246 +47,226 @@ 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', {
emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' }
});
return this.UserFields.sync({ force: true }).then(() => {
return this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] }
}).then(user => {
expect(user.emergencyContact.name).to.equal('joe');
});
await this.UserFields.sync({ force: true });
const user = await this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] }
});
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', {
emergencyContact: { type: DataTypes.JSON, field: 'emergy_contact' }
});
return this.UserFields.sync({ force: true }).then(() => {
return this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] }
}).then(user => {
user.emergencyContact = { name: 'larry' };
return user.save();
}).then(user => {
expect(user.emergencyContact.name).to.equal('larry');
});
await this.UserFields.sync({ force: true });
const user0 = await this.UserFields.create({
emergencyContact: { name: 'joe', phones: [1337, 42] }
});
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 };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({ where: { username: 'swen' }, attributes: ['emergency_contact'] });
})
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact);
});
const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
expect(user0.emergency_contact).to.eql(emergencyContact);
const user = await this.User.findOne({ where: { username: 'swen' }, attributes: ['emergency_contact'] });
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] };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.phones[1]'), 'firstEmergencyNumber']]
});
})
.then(user => {
expect(parseInt(user.getDataValue('firstEmergencyNumber'), 10)).to.equal(42);
});
const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
expect(user0.emergency_contact).to.eql(emergencyContact);
const user = await this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.phones[1]'), 'firstEmergencyNumber']]
});
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 };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate'), 'katesNumber']]
});
})
.then(user => {
expect(parseInt(user.getDataValue('katesNumber'), 10)).to.equal(1337);
});
const user0 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
expect(user0.emergency_contact).to.eql(emergencyContact);
const user = await this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate'), 'katesNumber']]
});
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] } };
return this.User.create({ username: 'swen', emergency_contact: emergencyContact })
.then(user => {
expect(user.emergency_contact).to.eql(emergencyContact);
return this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.email'), 'katesEmail']]
});
}).then(user => {
expect(user.getDataValue('katesEmail')).to.equal('kate@kate.com');
}).then(() => {
return this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.phones[1]'), 'katesFirstPhone']]
});
}).then(user => {
expect(parseInt(user.getDataValue('katesFirstPhone'), 10)).to.equal(42);
});
const user1 = await this.User.create({ username: 'swen', emergency_contact: emergencyContact });
expect(user1.emergency_contact).to.eql(emergencyContact);
const user0 = await this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.email'), 'katesEmail']]
});
expect(user0.getDataValue('katesEmail')).to.equal('kate@kate.com');
const user = await this.User.findOne({
where: { username: 'swen' },
attributes: [[Sequelize.json('emergency_contact.kate.phones[1]'), 'katesFirstPhone']]
});
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() {
return Promise.all([
it('should be able to retrieve a row based on the values of the json document', async function() {
await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => {
return this.User.findOne({
where: Sequelize.json('emergency_contact.name', 'kate'),
attributes: ['username', 'emergency_contact']
});
}).then(user => {
expect(user.emergency_contact.name).to.equal('kate');
]);
const user = await this.User.findOne({
where: Sequelize.json('emergency_contact.name', 'kate'),
attributes: ['username', 'emergency_contact']
});
expect(user.emergency_contact.name).to.equal('kate');
});
it('should be able to query using the nested query language', function() {
return Promise.all([
it('should be able to query using the nested query language', async function() {
await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => {
return this.User.findOne({
where: Sequelize.json({ emergency_contact: { name: 'kate' } })
});
}).then(user => {
expect(user.emergency_contact.name).to.equal('kate');
]);
const user = await this.User.findOne({
where: Sequelize.json({ emergency_contact: { name: 'kate' } })
});
expect(user.emergency_contact.name).to.equal('kate');
});
it('should be able to query using dot notation', function() {
return Promise.all([
it('should be able to query using dot notation', async function() {
await Promise.all([
this.User.create({ username: 'swen', emergency_contact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergency_contact: { name: 'joe' } })
]).then(() => {
return this.User.findOne({ where: Sequelize.json('emergency_contact.name', 'joe') });
}).then(user => {
expect(user.emergency_contact.name).to.equal('joe');
});
]);
const user = await this.User.findOne({ where: Sequelize.json('emergency_contact.name', 'joe') });
expect(user.emergency_contact.name).to.equal('joe');
});
it('should be able to query using dot notation with uppercase name', function() {
return Promise.all([
it('should be able to query using dot notation with uppercase name', async function() {
await Promise.all([
this.User.create({ username: 'swen', emergencyContact: { name: 'kate' } }),
this.User.create({ username: 'anna', emergencyContact: { name: 'joe' } })
]).then(() => {
return this.User.findOne({
attributes: [[Sequelize.json('emergencyContact.name'), 'contactName']],
where: Sequelize.json('emergencyContact.name', 'joe')
});
}).then(user => {
expect(user.get('contactName')).to.equal('joe');
]);
const user = await this.User.findOne({
attributes: [[Sequelize.json('emergencyContact.name'), 'contactName']],
where: Sequelize.json('emergencyContact.name', 'joe')
});
expect(user.get('contactName')).to.equal('joe');
});
it('should be able to query array using property accessor', function() {
return Promise.all([
it('should be able to query array using property accessor', async function() {
await Promise.all([
this.User.create({ username: 'swen', emergency_contact: ['kate', 'joe'] }),
this.User.create({ username: 'anna', emergency_contact: [{ name: 'joe' }] })
]).then(() => {
return this.User.findOne({ where: Sequelize.json('emergency_contact.0', 'kate') });
}).then(user => {
expect(user.username).to.equal('swen');
}).then(() => {
return this.User.findOne({ where: Sequelize.json('emergency_contact[0].name', 'joe') });
}).then(user => {
expect(user.username).to.equal('anna');
});
]);
const user0 = await this.User.findOne({ where: Sequelize.json('emergency_contact.0', 'kate') });
expect(user0.username).to.equal('swen');
const user = await this.User.findOne({ where: Sequelize.json('emergency_contact[0].name', 'joe') });
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';
return this.User.create({
const user0 = await this.User.create({
username: 'swen',
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';
return this.User.findOrCreate({
const user0 = await this.User.findOrCreate({
where: { username: 'swen' },
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
if (current.dialect.name === 'sqlite') {
it('should be able to find with just string', function() {
return this.User.create({
it('should be able to find with just string', async function() {
await this.User.create({
username: 'swen123',
emergency_contact: 'Unknown'
}).then(() => {
return this.User.findOne({ where: {
emergency_contact: 'Unknown'
} });
}).then(user => {
expect(user.username).to.equal('swen123');
});
const user = await this.User.findOne({ where: {
emergency_contact: 'Unknown'
} });
expect(user.username).to.equal('swen123');
});
}
it('should be able retrieve json value with nested include', function() {
return this.User.create({
it('should be able retrieve json value with nested include', async function() {
const user = await this.User.create({
emergency_contact: {
name: 'kate'
}
}).then(user => {
return this.Order.create({ UserId: user.id });
}).then(() => {
return this.Order.findAll({
attributes: ['id'],
include: [{
model: this.User,
attributes: [
[this.sequelize.json('emergency_contact.name'), 'katesName']
]
}]
});
}).then(orders => {
expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
});
await this.Order.create({ UserId: user.id });
const orders = await this.Order.findAll({
attributes: ['id'],
include: [{
model: this.User,
attributes: [
[this.sequelize.json('emergency_contact.name'), 'katesName']
]
}]
});
expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
});
});
}
if (current.dialect.supports.JSONB) {
describe('jsonb', () => {
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
emergency_contact: DataTypes.JSONB
......@@ -296,29 +274,29 @@ describe('model', () => {
this.Order = this.sequelize.define('Order');
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() {
return this.User.create({
it('should be able retrieve json value with nested include', async function() {
const user = await this.User.create({
emergency_contact: {
name: 'kate'
}
}).then(user => {
return this.Order.create({ UserId: user.id });
}).then(() => {
return this.Order.findAll({
attributes: ['id'],
include: [{
model: this.User,
attributes: [
[this.sequelize.json('emergency_contact.name'), 'katesName']
]
}]
});
}).then(orders => {
expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
});
await this.Order.create({ UserId: user.id });
const orders = await this.Order.findAll({
attributes: ['id'],
include: [{
model: this.User,
attributes: [
[this.sequelize.json('emergency_contact.name'), 'katesName']
]
}]
});
expect(orders[0].User.getDataValue('katesName')).to.equal('kate');
});
});
}
......
......@@ -284,13 +284,16 @@ describe(Support.getTestDialectTeaser('Model'), () => {
});
await User.sync({ force: true });
await Promise.all([
User.create({ username: 'tobi', email: 'tobi@tobi.me' }),
User.create({ username: 'tobi', email: 'tobi@tobi.me' })
]).catch(err => {
try {
await Promise.all([
User.create({ username: 'tobi', email: 'tobi@tobi.me' }),
User.create({ username: 'tobi', email: 'tobi@tobi.me' })
]);
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
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
......@@ -319,13 +322,15 @@ describe(Support.getTestDialectTeaser('Model'), () => {
email: { type: Sequelize.STRING, unique: 'user_and_email_index' }
});
await Promise.all([
User.create({ user_id: 1, email: 'tobi@tobi.me' }),
User.create({ user_id: 1, email: 'tobi@tobi.me' })
]).catch(err => {
try {
await Promise.all([
User.create({ user_id: 1, email: 'tobi@tobi.me' }),
User.create({ user_id: 1, email: 'tobi@tobi.me' })
]);
} catch (err) {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err.message).to.equal('User and email must be unique');
});
}
});
it('should allow the user to specify indexes in options', async function() {
......@@ -1934,9 +1939,12 @@ describe(Support.getTestDialectTeaser('Model'), () => {
});
afterEach(async function() {
await this.sequelize.dropSchema('schema_test')
.finally(() => this.sequelize.dropSchema('special'))
.finally(() => this.sequelize.dropSchema('prefix'));
try {
await this.sequelize.dropSchema('schema_test');
} finally {
await this.sequelize.dropSchema('special');
await this.sequelize.dropSchema('prefix');
}
});
it('should be able to drop with schemas', async function() {
......@@ -2065,80 +2073,79 @@ describe(Support.getTestDialectTeaser('Model'), () => {
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;
return this.UserPublic.sync({ force: true }).then(UserPublicSync => {
return UserPublicSync.create({ age: 3 }, {
logging: UserPublic => {
logged++;
if (dialect === 'postgres') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('"special"."UserSpecials"');
expect(UserPublic).to.include('INSERT INTO "UserPublics"');
} else if (dialect === 'sqlite') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special.UserSpecials`');
expect(UserPublic).to.include('INSERT INTO `UserPublics`');
} else if (dialect === 'mssql') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('[special].[UserSpecials]');
expect(UserPublic).to.include('INSERT INTO [UserPublics]');
} else if (dialect === 'mariadb') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special`.`UserSpecials`');
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1);
} else {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special.UserSpecials`');
expect(UserPublic).to.include('INSERT INTO `UserPublics`');
}
const UserPublicSync = await this.UserPublic.sync({ force: true });
await UserPublicSync.create({ age: 3 }, {
logging: UserPublic => {
logged++;
if (dialect === 'postgres') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('"special"."UserSpecials"');
expect(UserPublic).to.include('INSERT INTO "UserPublics"');
} else if (dialect === 'sqlite') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special.UserSpecials`');
expect(UserPublic).to.include('INSERT INTO `UserPublics`');
} else if (dialect === 'mssql') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('[special].[UserSpecials]');
expect(UserPublic).to.include('INSERT INTO [UserPublics]');
} else if (dialect === 'mariadb') {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special`.`UserSpecials`');
expect(UserPublic.indexOf('INSERT INTO `UserPublics`')).to.be.above(-1);
} else {
expect(this.UserSpecialSync.getTableName().toString()).to.equal('`special.UserSpecials`');
expect(UserPublic).to.include('INSERT INTO `UserPublics`');
}
}).then(() => {
return this.UserSpecialSync.schema('special').create({ age: 3 }, {
logging(UserSpecial) {
logged++;
if (dialect === 'postgres') {
expect(UserSpecial).to.include('INSERT INTO "special"."UserSpecials"');
} else if (dialect === 'sqlite') {
expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`');
} else if (dialect === 'mssql') {
expect(UserSpecial).to.include('INSERT INTO [special].[UserSpecials]');
} else if (dialect === 'mariadb') {
expect(UserSpecial).to.include('INSERT INTO `special`.`UserSpecials`');
} else {
expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`');
}
}
}).then(UserSpecial => {
return UserSpecial.update({ age: 5 }, {
logging(user) {
logged++;
if (dialect === 'postgres') {
expect(user).to.include('UPDATE "special"."UserSpecials"');
} else if (dialect === 'mssql') {
expect(user).to.include('UPDATE [special].[UserSpecials]');
} else if (dialect === 'mariadb') {
expect(user).to.include('UPDATE `special`.`UserSpecials`');
} else {
expect(user).to.include('UPDATE `special.UserSpecials`');
}
}
});
});
}).then(() => {
expect(logged).to.equal(3);
});
}
});
const UserSpecial = await this.UserSpecialSync.schema('special').create({ age: 3 }, {
logging(UserSpecial) {
logged++;
if (dialect === 'postgres') {
expect(UserSpecial).to.include('INSERT INTO "special"."UserSpecials"');
} else if (dialect === 'sqlite') {
expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`');
} else if (dialect === 'mssql') {
expect(UserSpecial).to.include('INSERT INTO [special].[UserSpecials]');
} else if (dialect === 'mariadb') {
expect(UserSpecial).to.include('INSERT INTO `special`.`UserSpecials`');
} else {
expect(UserSpecial).to.include('INSERT INTO `special.UserSpecials`');
}
}
});
await UserSpecial.update({ age: 5 }, {
logging(user) {
logged++;
if (dialect === 'postgres') {
expect(user).to.include('UPDATE "special"."UserSpecials"');
} else if (dialect === 'mssql') {
expect(user).to.include('UPDATE [special].[UserSpecials]');
} else if (dialect === 'mariadb') {
expect(user).to.include('UPDATE `special`.`UserSpecials`');
} else {
expect(user).to.include('UPDATE `special.UserSpecials`');
}
}
});
expect(logged).to.equal(3);
});
});
describe('references', () => {
beforeEach(function() {
beforeEach(async function() {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING });
return this.sequelize.getQueryInterface().dropTable('posts', { force: true }).then(() => {
return this.sequelize.getQueryInterface().dropTable('authors', { force: true });
}).then(() => {
return this.Author.sync();
});
await this.sequelize.getQueryInterface().dropTable('posts', { force: true });
await this.sequelize.getQueryInterface().dropTable('authors', { force: true });
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 Post = this.sequelize.define('post', {
......@@ -2150,7 +2157,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter.
return Post.sync({ logging: _.once(sql => {
await Post.sync({ logging: _.once(sql => {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (dialect === 'mysql' || dialect === 'mariadb') {
......@@ -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 Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn });
......@@ -2174,7 +2181,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter.
return Post.sync({ logging: _.once(sql => {
await Post.sync({ logging: _.once(sql => {
if (dialect === 'postgres') {
expect(sql).to.match(/"authorId" INTEGER REFERENCES "authors" \("id"\)/);
} else if (dialect === 'mysql' || dialect === 'mariadb') {
......@@ -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 Post = this.sequelize.define('post', { title: Sequelize.STRING, authorId: authorIdColumn });
......@@ -2197,8 +2204,9 @@ describe(Support.getTestDialectTeaser('Model'), () => {
this.Author.hasMany(Post);
Post.belongsTo(this.Author);
// The posts table gets dropped in the before filter.
return Post.sync().then(() => {
try {
// The posts table gets dropped in the before filter.
await Post.sync();
if (dialect === 'sqlite') {
// sorry ... but sqlite is too stupid to understand whats going on ...
expect(1).to.equal(1);
......@@ -2206,9 +2214,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
// the parser should not end up here ...
expect(2).to.equal(1);
}
return;
}).catch(err => {
} catch (err) {
if (dialect === 'mysql') {
// MySQL 5.7 or above doesn't support POINT EMPTY
if (semver.gte(current.options.databaseVersion, '5.6.0')) {
......@@ -2228,10 +2234,10 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} else {
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
const Member = this.sequelize.define('Member', {});
const idColumn = {
......@@ -2245,47 +2251,45 @@ describe(Support.getTestDialectTeaser('Model'), () => {
this.sequelize.define('Profile', { id: idColumn });
return this.sequelize.sync({ force: true });
await this.sequelize.sync({ force: true });
});
});
describe('blob', () => {
beforeEach(function() {
beforeEach(async function() {
this.BlobUser = this.sequelize.define('blobUser', {
data: Sequelize.BLOB
});
return this.BlobUser.sync({ force: true });
await this.BlobUser.sync({ force: true });
});
describe('buffers', () => {
it('should be able to take a buffer as parameter to a BLOB field', function() {
return this.BlobUser.create({
it('should be able to take a buffer as parameter to a BLOB field', async function() {
const user = await this.BlobUser.create({
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() {
return this.BlobUser.create({
it('should return a buffer when fetching a blob', async function() {
const user = await this.BlobUser.create({
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() {
return this.BlobUser.create({
it('should work when the database returns null', async function() {
const user = await this.BlobUser.create({
// 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'), () => {
// data is passed in, in string form? Very unclear, and very different.
describe('strings', () => {
it('should be able to take a string as parameter to a BLOB field', function() {
return this.BlobUser.create({
it('should be able to take a string as parameter to a BLOB field', async function() {
const user = await this.BlobUser.create({
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() {
return this.BlobUser.create({
it('should return a buffer when fetching a BLOB, even when the BLOB was inserted as a string', async function() {
const user = await this.BlobUser.create({
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'), () => {
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.Project = this.sequelize.define('Project', { title: DataTypes.STRING }, { paranoid: true });
this.Project.belongsToMany(this.User, { through: 'project_user' });
this.User.belongsToMany(this.Project, { through: 'project_user' });
return this.sequelize.sync({ force: true }).then(() => {
return this.User.bulkCreate([{
username: 'leia'
}, {
username: 'luke'
}, {
username: 'vader'
}]).then(() => {
return this.Project.bulkCreate([{
title: 'republic'
}, {
title: 'empire'
}]).then(() => {
return this.User.findAll().then(users => {
return this.Project.findAll().then(projects => {
const leia = users[0],
luke = users[1],
vader = users[2],
republic = projects[0],
empire = projects[1];
return leia.setProjects([republic]).then(() => {
return luke.setProjects([republic]).then(() => {
return vader.setProjects([empire]).then(() => {
return leia.destroy();
});
});
});
});
});
});
});
});
await this.sequelize.sync({ force: true });
await this.User.bulkCreate([{
username: 'leia'
}, {
username: 'luke'
}, {
username: 'vader'
}]);
await this.Project.bulkCreate([{
title: 'republic'
}, {
title: 'empire'
}]);
const users = await this.User.findAll();
const projects = await this.Project.findAll();
const leia = users[0],
luke = users[1],
vader = users[2],
republic = projects[0],
empire = projects[1];
await leia.setProjects([republic]);
await luke.setProjects([republic]);
await vader.setProjects([empire]);
await leia.destroy();
});
it('should not fail when array contains Sequelize.or / and', function() {
return this.User.findAll({
it('should not fail when array contains Sequelize.or / and', async function() {
const res = await this.User.findAll({
where: [
this.sequelize.or({ username: 'vader' }, { username: 'luke' }),
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() {
return expect(this.User.findAll({
it('should fail when array contains strings', async function() {
await expect(this.User.findAll({
where: ['this is a mistake', ['dont do it!']]
})).to.eventually.be.rejectedWith(Error, 'Support for literal replacements in the `where` object has been removed.');
});
it('should not fail with an include', function() {
return this.User.findAll({
it('should not fail with an include', async function() {
const users = await this.User.findAll({
where: this.sequelize.literal(`${this.sequelize.queryInterface.queryGenerator.quoteIdentifiers('Projects.title')} = ${this.sequelize.queryInterface.queryGenerator.escape('republic')}`),
include: [
{ model: this.Project }
]
}).then(users => {
expect(users.length).to.be.equal(1);
expect(users[0].username).to.be.equal('luke');
});
expect(users.length).to.be.equal(1);
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 = '';
if (this.User.name) {
tableName = `${this.sequelize.queryInterface.queryGenerator.quoteIdentifier(this.User.name)}.`;
}
return this.User.findAll({
const users = await this.User.findAll({
paranoid: false,
where: this.sequelize.literal(`${tableName + this.sequelize.queryInterface.queryGenerator.quoteIdentifier('deletedAt')} IS NOT NULL `),
include: [
{ model: this.Project }
]
}).then(users => {
expect(users.length).to.be.equal(1);
expect(users[0].username).to.be.equal('leia');
});
expect(users.length).to.be.equal(1);
expect(users[0].username).to.be.equal('leia');
});
it('should not overwrite a specified deletedAt (complex query) by setting paranoid: false', function() {
return this.User.findAll({
it('should not overwrite a specified deletedAt (complex query) by setting paranoid: false', async function() {
const res = await this.User.findAll({
paranoid: false,
where: [
this.sequelize.or({ username: 'leia' }, { username: 'luke' }),
......@@ -2419,98 +2418,94 @@ describe(Support.getTestDialectTeaser('Model'), () => {
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) {
it('supports multiple async transactions', function() {
it('supports multiple async transactions', async function() {
this.timeout(90000);
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
const User = sequelize.define('User', { username: Sequelize.STRING });
const testAsync = function() {
return sequelize.transaction().then(t => {
return User.create({
username: 'foo'
}, {
transaction: t
}).then(() => {
return User.findAll({
where: {
username: 'foo'
}
}).then(users => {
expect(users).to.have.length(0);
});
}).then(() => {
return User.findAll({
where: {
username: 'foo'
},
transaction: t
}).then(users => {
expect(users).to.have.length(1);
});
}).then(() => {
return t;
});
}).then(t => {
return t.rollback();
});
};
return User.sync({ force: true }).then(() => {
const tasks = [];
for (let i = 0; i < 1000; i++) {
tasks.push(testAsync);
const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { username: Sequelize.STRING });
const testAsync = async function() {
const t0 = await sequelize.transaction();
await User.create({
username: 'foo'
}, {
transaction: t0
});
const users0 = await User.findAll({
where: {
username: 'foo'
}
return pMap(tasks, entry => {
return entry();
}, {
// 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
});
});
expect(users0).to.have.length(0);
const users = await User.findAll({
where: {
username: 'foo'
},
transaction: t0
});
expect(users).to.have.length(1);
const t = t0;
return t.rollback();
};
await User.sync({ force: true });
const tasks = [];
for (let i = 0; i < 1000; i++) {
tasks.push(testAsync);
}
await pMap(tasks, entry => {
return entry();
}, {
// 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
});
});
}
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', {
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/);
})) });
});
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', {
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/);
})) });
});
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', {
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/);
})) });
});
});
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', {
UserId: {
type: Sequelize.STRING,
......@@ -2534,11 +2529,11 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}
});
return this.sequelize.sync({ force: true });
await this.sequelize.sync({ force: true });
});
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 },
{ username: null },
{ username: null }];
......@@ -2554,15 +2549,14 @@ describe(Support.getTestDialectTeaser('Model'), () => {
}
});
return this.sequelize.sync({ force: true }).then(() => {
expect(user.bulkCreate(data, {
validate: true,
individualHooks: true
})).to.be.rejectedWith(errors.AggregateError);
});
await this.sequelize.sync({ force: true });
expect(user.bulkCreate(data, {
validate: true,
individualHooks: true
})).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', {
username: {
type: Sequelize.STRING,
......@@ -2582,13 +2576,10 @@ describe(Support.getTestDialectTeaser('Model'), () => {
});
const data = [{ username: 'jon' }];
return this.sequelize.sync({ force: true }).then(() => {
return user.bulkCreate(data).then(() => {
return user.findAll().then(users1 => {
expect(users1[0].username).to.equal('jon');
});
});
});
await this.sequelize.sync({ force: true });
await user.bulkCreate(data);
const users1 = await user.findAll();
expect(users1[0].username).to.equal('jon');
});
});
});
......@@ -13,7 +13,7 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
let sandbox;
let readSpy, writeSpy;
beforeEach(function() {
beforeEach(async function() {
sandbox = sinon.createSandbox();
this.sequelize = Support.getSequelizeInstance(null, null, null, {
......@@ -33,11 +33,9 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
}
});
return this.User.sync({ force: true })
.then(() => {
readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire');
writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire');
});
await this.User.sync({ force: true });
readSpy = sandbox.spy(this.sequelize.connectionManager.pool.read, 'acquire');
writeSpy = sandbox.spy(this.sequelize.connectionManager.pool.write, 'acquire');
});
afterEach(() => {
......@@ -54,25 +52,25 @@ describe(Support.getTestDialectTeaser('Replication'), () => {
chai.expect(readSpy.notCalled).eql(true);
}
it('should be able to make a write', function() {
return this.User.create({
it('should be able to make a write', async function() {
await expectWriteCalls(await this.User.create({
firstName: Math.random().toString()
}).then(expectWriteCalls);
}));
});
it('should be able to make a read', function() {
return this.User.findAll().then(expectReadCalls);
it('should be able to make a read', async function() {
await expectReadCalls(await this.User.findAll());
});
it('should run read-only transactions on the replica', function() {
return this.sequelize.transaction({ readOnly: true }, transaction => {
it('should run read-only transactions on the replica', async function() {
await expectReadCalls(await this.sequelize.transaction({ readOnly: true }, transaction => {
return this.User.findAll({ transaction });
}).then(expectReadCalls);
}));
});
it('should run non-read-only transactions on the primary', function() {
return this.sequelize.transaction(transaction => {
it('should run non-read-only transactions on the primary', async function() {
await expectWriteCalls(await this.sequelize.transaction(transaction => {
return this.User.findAll({ transaction });
}).then(expectWriteCalls);
}));
});
});
......@@ -6,43 +6,37 @@ const chai = require('chai'),
DataTypes = require('../../lib/data-types');
describe(Support.getTestDialectTeaser('Schema'), () => {
beforeEach(function() {
return this.sequelize.createSchema('testschema');
beforeEach(async function() {
await this.sequelize.createSchema('testschema');
});
afterEach(function() {
return this.sequelize.dropSchema('testschema');
afterEach(async function() {
await this.sequelize.dropSchema('testschema');
});
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('User', {
aNumber: { type: DataTypes.INTEGER }
}, {
schema: 'testschema'
});
return this.User.sync({ force: true });
await this.User.sync({ force: true });
});
it('supports increment', function() {
return this.User.create({ aNumber: 1 }).then(user => {
return user.increment('aNumber', { by: 3 });
}).then(result => {
return result.reload();
}).then(user => {
expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(4);
});
it('supports increment', async function() {
const user0 = await this.User.create({ aNumber: 1 });
const result = await user0.increment('aNumber', { by: 3 });
const user = await result.reload();
expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(4);
});
it('supports decrement', function() {
return this.User.create({ aNumber: 10 }).then(user => {
return user.decrement('aNumber', { by: 3 });
}).then(result => {
return result.reload();
}).then(user => {
expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(7);
});
it('supports decrement', async function() {
const user0 = await this.User.create({ aNumber: 10 });
const result = await user0.decrement('aNumber', { by: 3 });
const user = await result.reload();
expect(user).to.be.ok;
expect(user.aNumber).to.be.equal(7);
});
});
......@@ -77,8 +77,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (dialect !== 'sqlite') {
describe('authenticate', () => {
describe('with valid credentials', () => {
it('triggers the success event', function() {
return this.sequelize.authenticate();
it('triggers the success event', async function() {
await this.sequelize.authenticate();
});
});
......@@ -88,41 +88,44 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.sequelizeWithInvalidConnection = new Sequelize('wat', 'trololo', 'wow', options);
});
it('triggers the error event', function() {
return this
.sequelizeWithInvalidConnection
.authenticate()
.catch(err => {
expect(err).to.not.be.null;
});
it('triggers the error event', async function() {
try {
await this
.sequelizeWithInvalidConnection
.authenticate();
} catch (err) {
expect(err).to.not.be.null;
}
});
it('triggers an actual RangeError or ConnectionError', function() {
return this
.sequelizeWithInvalidConnection
.authenticate()
.catch(err => {
expect(
err instanceof RangeError ||
err instanceof Sequelize.ConnectionError
).to.be.ok;
});
it('triggers an actual RangeError or ConnectionError', async function() {
try {
await this
.sequelizeWithInvalidConnection
.authenticate();
} catch (err) {
expect(
err instanceof RangeError ||
err instanceof Sequelize.ConnectionError
).to.be.ok;
}
});
it('triggers the actual adapter error', function() {
return this
.sequelizeWithInvalidConnection
.authenticate()
.catch(err => {
console.log(err);
expect(
err.message.includes('connect ECONNREFUSED') ||
err.message.includes('invalid port number') ||
err.message.match(/should be >=? 0 and < 65536/) ||
err.message.includes('Login failed for user') ||
err.message.includes('must be > 0 and < 65536')
).to.be.ok;
});
it('triggers the actual adapter error', async function() {
try {
await this
.sequelizeWithInvalidConnection
.authenticate();
} catch (err) {
console.log(err);
expect(
err.message.includes('connect ECONNREFUSED') ||
err.message.includes('invalid port number') ||
err.message.match(/should be >=? 0 and < 65536/) ||
err.message.includes('Login failed for user') ||
err.message.includes('must be > 0 and < 65536')
).to.be.ok;
}
});
});
......@@ -131,38 +134,41 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.sequelizeWithInvalidCredentials = new Sequelize('localhost', 'wtf', 'lol', this.sequelize.options);
});
it('triggers the error event', function() {
return this
.sequelizeWithInvalidCredentials
.authenticate()
.catch(err => {
expect(err).to.not.be.null;
});
it('triggers the error event', async function() {
try {
await this
.sequelizeWithInvalidCredentials
.authenticate();
} catch (err) {
expect(err).to.not.be.null;
}
});
it('triggers an actual sequlize error', function() {
return this
.sequelizeWithInvalidCredentials
.authenticate()
.catch(err => {
expect(err).to.be.instanceof(Sequelize.Error);
});
it('triggers an actual sequlize error', async function() {
try {
await this
.sequelizeWithInvalidCredentials
.authenticate();
} catch (err) {
expect(err).to.be.instanceof(Sequelize.Error);
}
});
it('triggers the error event when using replication', () => {
return new Sequelize('sequelize', null, null, {
dialect,
replication: {
read: {
host: 'localhost',
username: 'omg',
password: 'lol'
it('triggers the error event when using replication', async () => {
try {
await new Sequelize('sequelize', null, null, {
dialect,
replication: {
read: {
host: 'localhost',
username: 'omg',
password: 'lol'
}
}
}
}).authenticate()
.catch(err => {
expect(err).to.not.be.null;
});
}).authenticate();
} catch (err) {
expect(err).to.not.be.null;
}
});
});
});
......@@ -221,7 +227,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
console.log.restore && console.log.restore();
});
beforeEach(function() {
beforeEach(async function() {
this.User = this.sequelize.define('User', {
username: {
type: DataTypes.STRING,
......@@ -237,37 +243,37 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
qq('createdAt') }, ${qq('updatedAt')
}) 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() {
return this.sequelize.query(this.insertQuery, { raw: true });
it('executes a query the internal way', async function() {
await this.sequelize.query(this.insertQuery, { raw: true });
});
it('executes a query if only the sql is passed', function() {
return this.sequelize.query(this.insertQuery);
it('executes a query if only the sql is passed', async function() {
await this.sequelize.query(this.insertQuery);
});
it('executes a query if a placeholder value is an array', function() {
return this.sequelize.query(`INSERT INTO ${qq(this.User.tableName)} (username, email_address, ` +
it('executes a query if a placeholder value is an array', async function() {
await this.sequelize.query(`INSERT INTO ${qq(this.User.tableName)} (username, email_address, ` +
`${qq('createdAt')}, ${qq('updatedAt')}) VALUES ?;`, {
replacements: [[
['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']
]]
})
.then(() => this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: this.sequelize.QueryTypes.SELECT
}))
.then(rows => {
expect(rows).to.be.lengthOf(2);
expect(rows[0].username).to.be.equal('john');
expect(rows[1].username).to.be.equal('michael');
});
});
const rows = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: this.sequelize.QueryTypes.SELECT
});
expect(rows).to.be.lengthOf(2);
expect(rows[0].username).to.be.equal('john');
expect(rows[1].username).to.be.equal('michael');
});
describe('retry', () => {
it('properly bind parameters on extra retries', function() {
it('properly bind parameters on extra retries', async function() {
const payload = {
username: 'test',
createdAt: '2010-10-10 00:00:00',
......@@ -276,7 +282,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
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);
`, {
bind: payload,
......@@ -287,41 +295,40 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
/Validation/
]
}
}))).to.be.rejectedWith(Sequelize.UniqueConstraintError).then(() => {
expect(spy.callCount).to.eql(3);
});
})).to.be.rejectedWith(Sequelize.UniqueConstraintError);
expect(spy.callCount).to.eql(3);
});
});
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 sequelize = Support.createSequelizeInstance({
logging: logger,
benchmark: true
});
return sequelize.query('select 1;').then(() => {
expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \((\d*|default)\): select 1/);
expect(typeof logger.args[0][1] === 'number').to.be.true;
});
await sequelize.query('select 1;');
expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \((\d*|default)\): select 1/);
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();
return this.sequelize.query('select 1;', {
await this.sequelize.query('select 1;', {
logging: logger,
benchmark: true
}).then(() => {
expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \(\d*|default\): select 1;/);
expect(typeof logger.args[0][1] === 'number').to.be.true;
});
expect(logger.calledOnce).to.be.true;
expect(logger.args[0][0]).to.be.match(/Executed \(\d*|default\): select 1;/);
expect(typeof logger.args[0][1] === 'number').to.be.true;
});
describe('log sql when set logQueryParameters', () => {
beforeEach(function() {
beforeEach(async function() {
this.sequelize = Support.createSequelizeInstance({
benchmark: true,
logQueryParameters: true
......@@ -342,175 +349,163 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
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;
return this.User.create({
const user = await this.User.create({
username: 'john',
emailAddress: 'john@gmail.com'
}, {
logging: s =>{
createSql = s;
}
}).then(user=>{
user.username = 'li';
return user.save({
logging: s =>{
updateSql = s;
}
});
}).then(()=>{
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})/);
});
user.username = 'li';
await user.save({
logging: s =>{
updateSql = s;
}
});
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})/);
});
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;
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 })
.then(()=>{
expect(logSql).to.match(/; ("foo", "bar"|{"(\$1|0)":"foo","(\$2|1)":"bar"})/);
});
await this.sequelize.query(`select $1${typeCast} as foo, $2${typeCast} as bar`, { bind: ['foo', 'bar'], logging: s=>logSql = s });
expect(logSql).to.match(/; ("foo", "bar"|{"(\$1|0)":"foo","(\$2|1)":"bar"})/);
});
});
});
it('executes select queries correctly', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query(`select * from ${qq(this.User.tableName)}`);
}).then(([users]) => {
expect(users.map(u => { return u.username; })).to.include('john');
});
it('executes select queries correctly', async function() {
await this.sequelize.query(this.insertQuery);
const [users] = await this.sequelize.query(`select * from ${qq(this.User.tableName)}`);
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);
seq.options.quoteIdentifiers = false;
return seq.query(this.insertQuery).then(() => {
return seq.query(`select * from ${qq(this.User.tableName)}`);
}).then(([users]) => {
expect(users.map(u => { return u.username; })).to.include('john');
});
await seq.query(this.insertQuery);
const [users] = await seq.query(`select * from ${qq(this.User.tableName)}`);
expect(users.map(u => { return u.username; })).to.include('john');
});
it('executes select query with dot notation results', function() {
return this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`).then(() => {
return this.sequelize.query(this.insertQuery);
}).then(() => {
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' }]);
});
it('executes select query with dot notation results', async function() {
await this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`);
await this.sequelize.query(this.insertQuery);
const [users] = await this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`);
expect(users).to.deep.equal([{ 'user.username': 'john' }]);
});
it('executes select query with dot notation results and nest it', function() {
return this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`).then(() => {
return this.sequelize.query(this.insertQuery);
}).then(() => {
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' }]);
});
it('executes select query with dot notation results and nest it', async function() {
await this.sequelize.query(`DELETE FROM ${qq(this.User.tableName)}`);
await this.sequelize.query(this.insertQuery);
const users = await this.sequelize.query(`select username as ${qq('user.username')} from ${qq(this.User.tableName)}`, { raw: true, nest: true });
expect(users.map(u => { return u.user; })).to.deep.equal([{ 'username': 'john' }]);
});
if (dialect === 'mysql') {
it('executes stored procedures', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query('DROP PROCEDURE IF EXISTS foo').then(() => {
return this.sequelize.query(
`CREATE PROCEDURE foo()\nSELECT * FROM ${this.User.tableName};`
).then(() => {
return this.sequelize.query('CALL foo()').then(users => {
expect(users.map(u => { return u.username; })).to.include('john');
});
});
});
});
it('executes stored procedures', async function() {
await this.sequelize.query(this.insertQuery);
await this.sequelize.query('DROP PROCEDURE IF EXISTS foo');
await this.sequelize.query(
`CREATE PROCEDURE foo()\nSELECT * FROM ${this.User.tableName};`
);
const users = await this.sequelize.query('CALL foo()');
expect(users.map(u => { return u.username; })).to.include('john');
});
} else {
console.log('FIXME: I want to be supported in this dialect as well :-(');
}
it('uses the passed model', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User
});
}).then(users => {
expect(users[0]).to.be.instanceof(this.User);
it('uses the passed model', async function() {
await this.sequelize.query(this.insertQuery);
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User
});
expect(users[0]).to.be.instanceof(this.User);
});
it('maps the field names to attributes based on the passed model', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User,
mapToModel: true
});
}).then(users => {
expect(users[0].emailAddress).to.be.equal('john@gmail.com');
it('maps the field names to attributes based on the passed model', async function() {
await this.sequelize.query(this.insertQuery);
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
model: this.User,
mapToModel: true
});
expect(users[0].emailAddress).to.be.equal('john@gmail.com');
});
it('arbitrarily map the field names', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT',
fieldMap: { username: 'userName', email_address: 'email' }
});
}).then(users => {
expect(users[0].userName).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com');
it('arbitrarily map the field names', async function() {
await this.sequelize.query(this.insertQuery);
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT',
fieldMap: { username: 'userName', email_address: 'email' }
});
expect(users[0].userName).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com');
});
it('keeps field names that are mapped to the same name', function() {
return this.sequelize.query(this.insertQuery).then(() => {
return this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT',
fieldMap: { username: 'username', email_address: 'email' }
});
}).then(users => {
expect(users[0].username).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com');
it('keeps field names that are mapped to the same name', async function() {
await this.sequelize.query(this.insertQuery);
const users = await this.sequelize.query(`SELECT * FROM ${qq(this.User.tableName)};`, {
type: 'SELECT',
fieldMap: { username: 'username', email_address: 'email' }
});
expect(users[0].username).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com');
});
it('reject if `values` and `options.replacements` are both passed', function() {
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { raw: true, replacements: [1, 2] })
it('reject if `values` and `options.replacements` are both passed', async function() {
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');
});
it('reject if `sql.bind` and `options.bind` are both passed', function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, bind: [1, 2] })
it('reject if `sql.bind` and `options.bind` are both passed', async function() {
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');
});
it('reject if `options.replacements` and `options.bind` are both passed', function() {
return this.sequelize.query('select $1 + ? as foo, $2 + ? as bar', { raw: true, bind: [1, 2], replacements: [1, 2] })
it('reject if `options.replacements` and `options.bind` are both passed', async function() {
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');
});
it('reject if `sql.bind` and `sql.values` are both passed', function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2], values: [1, 2] }, { raw: true })
it('reject if `sql.bind` and `sql.values` are both passed', async function() {
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');
});
it('reject if `sql.bind` and `options.replacements`` are both passed', function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $2 + ? as bar', bind: [1, 2] }, { raw: true, replacements: [1, 2] })
it('reject if `sql.bind` and `options.replacements`` are both passed', async function() {
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');
});
it('reject if `options.bind` and `sql.replacements` are both passed', function() {
return this.sequelize.query({ query: 'select $1 + ? as foo, $1 _ ? as bar', values: [1, 2] }, { raw: true, bind: [1, 2] })
it('reject if `options.bind` and `sql.replacements` are both passed', async function() {
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');
});
it('properly adds and escapes replacement value', function() {
it('properly adds and escapes replacement value', async function() {
let logSql;
const number = 1,
date = new Date(),
......@@ -519,7 +514,8 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
buffer = Buffer.from('t\'e"st');
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',
values: [number, date, string, boolean, buffer]
}, {
......@@ -527,25 +523,25 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
logging(s) {
logSql = s;
}
}).then(result => {
const res = result[0] || {};
res.date = res.date && new Date(res.date);
res.boolean = res.boolean && true;
if (typeof res.buffer === 'string' && res.buffer.startsWith('\\x')) {
res.buffer = Buffer.from(res.buffer.substring(2), 'hex');
}
expect(res).to.deep.equal({
number,
date,
string,
boolean,
buffer
});
expect(logSql).to.not.include('?');
});
const res = result[0] || {};
res.date = res.date && new Date(res.date);
res.boolean = res.boolean && true;
if (typeof res.buffer === 'string' && res.buffer.startsWith('\\x')) {
res.buffer = Buffer.from(res.buffer.substring(2), 'hex');
}
expect(res).to.deep.equal({
number,
date,
string,
boolean,
buffer
});
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;
class SQLStatement {
constructor() {
......@@ -555,274 +551,260 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
return 'select ? as foo, ? as bar';
}
}
return this.sequelize.query(new SQLStatement(), { type: this.sequelize.QueryTypes.SELECT, logging: s => logSql = s } ).then(result => {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
expect(logSql).to.not.include('?');
});
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(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;
return this.sequelize.query({ query: 'select ? as foo, ? as bar', values: [1, 2] }, { type: this.sequelize.QueryTypes.SELECT, logging(s) { logSql = s; } }).then(result => {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
expect(logSql).to.not.include('?');
});
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(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' : '';
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 => {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1');
expect(logSql).to.include('$2');
} else if (dialect === 'mssql') {
expect(logSql).to.include('@0');
expect(logSql).to.include('@1');
} else if (dialect === 'mysql') {
expect(logSql.match(/\?/g).length).to.equal(2);
}
});
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 }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1');
expect(logSql).to.include('$2');
} else if (dialect === 'mssql') {
expect(logSql).to.include('@0');
expect(logSql).to.include('@1');
} else if (dialect === 'mysql') {
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' ? '"' : '`',
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' ? '"' : '`',
sql = `select 1 as ${Sequelize.Utils.addTicks('foo.bar.baz', tickChar)}`;
return this.sequelize.query(sql, { raw: true, nest: true }).then(result => {
expect(result).to.deep.equal([{ foo: { bar: { baz: 1 } } }]);
});
const result = await this.sequelize.query(sql, { raw: true, nest: true });
expect(result).to.deep.equal([{ foo: { bar: { baz: 1 } } }]);
});
it('replaces token with the passed array', function() {
return this.sequelize.query('select ? as foo, ? as bar', { type: this.sequelize.QueryTypes.SELECT, replacements: [1, 2] }).then(result => {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
});
it('replaces token with the passed array', async function() {
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 }]);
});
it('replaces named parameters with the passed object', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: 2 } }).then(obj => obj[0]))
it('replaces named parameters with the passed object', async function() {
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 }]);
});
it('replaces named parameters with the passed object and ignore those which does not qualify', 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]))
it('replaces named parameters with the passed object and ignore those which does not qualify', async function() {
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' }]);
});
it('replaces named parameters with the passed object using the same key twice', 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]))
it('replaces named parameters with the passed object using the same key twice', async function() {
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 }]);
});
it('replaces named parameters with the passed object having a null property', function() {
return expect(this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: { one: 1, two: null } }).then(obj => obj[0]))
it('replaces named parameters with the passed object having a null property', async function() {
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 }]);
});
it('reject when key is missing in the passed object', function() {
return this.sequelize.query('select :one as foo, :two as bar, :three as baz', { raw: true, replacements: { one: 1, two: 2 } })
it('reject when key is missing in the passed object', async function() {
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);
});
it('reject with the passed number', function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 2 })
it('reject with the passed number', async function() {
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);
});
it('reject with the passed empty object', function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: {} })
it('reject with the passed empty object', async function() {
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);
});
it('reject with the passed string', function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: 'foobar' })
it('reject with the passed string', async function() {
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);
});
it('reject with the passed date', function() {
return this.sequelize.query('select :one as foo, :two as bar', { raw: true, replacements: new Date() })
it('reject with the passed date', async function() {
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);
});
it('binds token with the passed array', function() {
it('binds token with the passed array', async function() {
const typeCast = dialect === 'postgres' ? '::int' : '';
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 => {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1');
}
});
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 }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
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' : '';
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 => {
expect(result[0]).to.deep.equal([{ foo: 1, bar: 2 }]);
if (dialect === 'postgres') {
expect(logSql).to.include('$1');
}
if (dialect === 'sqlite') {
expect(logSql).to.include('$one');
}
});
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 }]);
if (dialect === 'postgres') {
expect(logSql).to.include('$1');
}
if (dialect === 'sqlite') {
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' : '';
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 => {
expect(result[0]).to.deep.equal([{ foo: 1, bar: 2, baz: 1 }]);
if (dialect === 'postgres') {
expect(logSql).to.include('$1');
expect(logSql).to.include('$2');
expect(logSql).to.not.include('$3');
}
});
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 }]);
if (dialect === 'postgres') {
expect(logSql).to.include('$1');
expect(logSql).to.include('$2');
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' : '';
return this.sequelize.query(`select $one${typeCast} as foo, $two${typeCast} as bar`, { raw: true, bind: { one: 1, two: null } }).then(result => {
expect(result[0]).to.deep.equal([{ foo: 1, bar: null }]);
});
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 }]);
});
it('binds named parameters array handles escaped $$', function() {
it('binds named parameters array handles escaped $$', async function() {
const typeCast = dialect === 'postgres' ? '::int' : '';
let logSql;
return this.sequelize.query(`select $1${typeCast} as foo, '$$ / $$1' as bar`, { raw: true, bind: [1], logging(s) { logSql = s;} }).then(result => {
expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $1' }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql).to.include('$1');
}
});
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' }]);
if (dialect === 'postgres' || dialect === 'sqlite') {
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' : '';
return this.sequelize.query(`select $one${typeCast} as foo, '$$ / $$one' as bar`, { raw: true, bind: { one: 1 } }).then(result => {
expect(result[0]).to.deep.equal([{ foo: 1, bar: '$ / $one' }]);
});
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' }]);
});
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' : '';
return this.sequelize.query(`select $one${typeCast} as foo$bar`, { raw: true, bind: { one: 1 } }).then(result => {
expect(result[0]).to.deep.equal([{ foo$bar: 1 }]);
});
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 }]);
});
if (dialect === 'postgres' || dialect === 'sqlite' || dialect === 'mssql') {
it('does not improperly escape arrays of strings bound to named parameters', function() {
return this.sequelize.query('select :stringArray as foo', { raw: true, replacements: { stringArray: ['"string"'] } }).then(result => {
expect(result[0]).to.deep.equal([{ foo: '"string"' }]);
});
it('does not improperly escape arrays of strings bound to named parameters', async function() {
const result = await this.sequelize.query('select :stringArray as foo', { raw: true, replacements: { stringArray: ['"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' : '';
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);
});
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' : '';
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);
});
it('reject when bind key is $0 with the passed array', function() {
return this.sequelize.query('select $1 as foo, $0 as bar, $3 as baz', { raw: true, bind: [1, 2] })
it('reject when bind key is $0 with the passed array', async function() {
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);
});
it('reject when bind key is $01 with the passed array', function() {
return this.sequelize.query('select $1 as foo, $01 as bar, $3 as baz', { raw: true, bind: [1, 2] })
it('reject when bind key is $01 with the passed array', async function() {
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);
});
it('reject when bind key is missing in the passed array', function() {
return this.sequelize.query('select $1 as foo, $2 as bar, $3 as baz', { raw: true, bind: [1, 2] })
it('reject when bind key is missing in the passed array', async function() {
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);
});
it('reject when bind key is missing in the passed object', function() {
return this.sequelize.query('select $one as foo, $two as bar, $three as baz', { raw: true, bind: { one: 1, two: 2 } })
it('reject when bind key is missing in the passed object', async function() {
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);
});
it('reject with the passed number for bind', function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 2 })
it('reject with the passed number for bind', async function() {
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);
});
it('reject with the passed empty object for bind', function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: {} })
it('reject with the passed empty object for bind', async function() {
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);
});
it('reject with the passed string for bind', function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: 'foobar' })
it('reject with the passed string for bind', async function() {
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);
});
it('reject with the passed date for bind', function() {
return this.sequelize.query('select $one as foo, $two as bar', { raw: true, bind: new Date() })
it('reject with the passed date for bind', async function() {
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);
});
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()';
if (dialect === 'mssql') {
datetime = 'GETDATE()';
}
return this.sequelize.query(`SELECT ${datetime} AS t`).then(([result]) => {
expect(moment(result[0].t).isValid()).to.be.true;
});
const [result] = await this.sequelize.query(`SELECT ${datetime} AS t`);
expect(moment(result[0].t).isValid()).to.be.true;
});
if (Support.getTestDialect() === 'postgres') {
it('replaces named parameters with the passed object and ignores casts', 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]))
it('replaces named parameters with the passed object and ignores casts', async function() {
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 }]);
});
it('supports WITH queries', 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]))
it('supports WITH queries', async function() {
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' }]);
});
}
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;
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(() => {
// sqlite.exec does not return a result
expect(logSql).to.not.include('$one');
expect(logSql).to.include('\'$$\'');
});
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
expect(logSql).to.not.include('$one');
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;
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(() => {
// sqlite.exec does not return a result
expect(logSql).to.not.include('$one');
expect(logSql).to.include('\'$$\'');
});
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
expect(logSql).to.not.include('$one');
expect(logSql).to.include('\'$$\'');
});
}
......@@ -892,34 +874,30 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
.to.be.rejectedWith(TypeError, 'options.transaction is required');
});
it('one value', function() {
return this.sequelize.transaction().then(t => {
this.t = t;
return this.sequelize.set({ foo: 'bar' }, { transaction: t });
}).then(() => {
return this.sequelize.query('SELECT @foo as `foo`', { plain: true, transaction: this.t });
}).then(data => {
expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar');
return this.t.commit();
});
it('one value', async function() {
const t = await this.sequelize.transaction();
this.t = t;
await this.sequelize.set({ foo: 'bar' }, { transaction: t });
const data = await this.sequelize.query('SELECT @foo as `foo`', { plain: true, transaction: this.t });
expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar');
await this.t.commit();
});
it('multiple values', function() {
return this.sequelize.transaction().then(t => {
this.t = t;
return this.sequelize.set({
foo: 'bar',
foos: 'bars'
}, { transaction: t });
}).then(() => {
return this.sequelize.query('SELECT @foo as `foo`, @foos as `foos`', { plain: true, transaction: this.t });
}).then(data => {
expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar');
expect(data.foos).to.be.equal('bars');
return this.t.commit();
});
it('multiple values', async function() {
const t = await this.sequelize.transaction();
this.t = t;
await this.sequelize.set({
foo: 'bar',
foos: 'bars'
}, { transaction: t });
const data = await this.sequelize.query('SELECT @foo as `foo`, @foos as `foos`', { plain: true, transaction: this.t });
expect(data).to.be.ok;
expect(data.foo).to.be.equal('bar');
expect(data.foos).to.be.equal('bars');
await this.t.commit();
});
});
}
......@@ -961,21 +939,19 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
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' });
return Photo.sync({ force: true }).then(() => {
return this.sequelize.getQueryInterface().showAllTables().then(tableNames => {
if (dialect === 'mssql' || dialect === 'mariadb') {
tableNames = tableNames.map(v => v.tableName);
}
expect(tableNames).to.include('photos');
});
});
await Photo.sync({ force: true });
let tableNames = await this.sequelize.getQueryInterface().showAllTables();
if (dialect === 'mssql' || dialect === 'mariadb') {
tableNames = tableNames.map(v => v.tableName);
}
expect(tableNames).to.include('photos');
});
});
describe('truncate', () => {
it('truncates all models', function() {
it('truncates all models', async function() {
const Project = this.sequelize.define(`project${config.rand()}`, {
id: {
type: DataTypes.INTEGER,
......@@ -985,47 +961,38 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
title: DataTypes.STRING
});
return this.sequelize.sync({ force: true }).then(() => {
return Project.create({ title: 'bla' });
}).then(project => {
expect(project).to.exist;
expect(project.title).to.equal('bla');
expect(project.id).to.equal(1);
return this.sequelize.truncate().then(() => {
return Project.findAll({});
});
}).then(projects => {
expect(projects).to.exist;
expect(projects).to.have.length(0);
});
await this.sequelize.sync({ force: true });
const project = await Project.create({ title: 'bla' });
expect(project).to.exist;
expect(project.title).to.equal('bla');
expect(project.id).to.equal(1);
await this.sequelize.truncate();
const projects = await Project.findAll({});
expect(projects).to.exist;
expect(projects).to.have.length(0);
});
});
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 Task = this.sequelize.define(`task${config.rand()}`, { title: DataTypes.STRING });
return Project.sync({ force: true }).then(() => {
return Task.sync({ force: true }).then(() => {
return Project.create({ title: 'bla' }).then(() => {
return Task.create({ title: 'bla' }).then(task => {
expect(task).to.exist;
expect(task.title).to.equal('bla');
});
});
});
});
await Project.sync({ force: true });
await Task.sync({ force: true });
await Project.create({ title: 'bla' });
const task = await Task.create({ title: 'bla' });
expect(task).to.exist;
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 });
return User.sync().then(() => {
expect(true).to.be.true;
});
await User.sync();
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', {
dialect: this.sequelize.options.dialect
});
......@@ -1033,43 +1000,44 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { 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/"');
});
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', {
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']));
const User2 = this.sequelizeWithInvalidCredentials.define('User', { name: DataTypes.STRING, bio: DataTypes.TEXT });
return User2.sync()
.then(() => { expect.fail(); })
.catch(err => {
if (dialect === 'postgres' || dialect === 'postgres-native') {
assert([
'fe_sendauth: no password supplied',
'role "bar" does not exist',
'FATAL: role "bar" does not exist',
'password authentication failed for user "bar"'
].includes(err.message.trim()));
} else if (dialect === 'mssql') {
expect(err.message).to.equal('Login failed for user \'bar\'.');
} else {
expect(err.message.toString()).to.match(/.*Access denied.*/);
}
});
try {
await User2.sync();
expect.fail();
} catch (err) {
if (dialect === 'postgres' || dialect === 'postgres-native') {
assert([
'fe_sendauth: no password supplied',
'role "bar" does not exist',
'FATAL: role "bar" does not exist',
'password authentication failed for user "bar"'
].includes(err.message.trim()));
} else if (dialect === 'mssql') {
expect(err.message).to.equal('Login failed for user \'bar\'.');
} else {
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', {
dialect: this.sequelize.options.dialect
});
......@@ -1077,10 +1045,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { 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', {
dialect: this.sequelize.options.dialect,
port: 99999
......@@ -1089,10 +1057,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { 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', {
dialect: this.sequelize.options.dialect,
port: 99999,
......@@ -1102,10 +1070,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
sequelize.define('Project', { 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', {
authorID: {
type: Sequelize.BIGINT,
......@@ -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', {
id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING
......@@ -1145,17 +1113,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
foreignKeyConstraint: true
});
return this.sequelize.sync();
await this.sequelize.sync();
});
}
it('return the sequelize instance after syncing', function() {
return this.sequelize.sync().then(sequelize => {
expect(sequelize).to.deep.equal(this.sequelize);
});
it('return the sequelize instance after syncing', async function() {
const sequelize = await this.sequelize.sync();
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', {
id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING
......@@ -1165,12 +1132,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
paranoid: false
});
return block.sync().then(result => {
expect(result).to.deep.equal(block);
});
const result = await block.sync();
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', {
user_id: {
type: DataTypes.INTEGER
......@@ -1179,7 +1145,7 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
underscored: true
});
return this.sequelize.sync({
await this.sequelize.sync({
alter: true
});
});
......@@ -1195,18 +1161,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
this.User = this.sequelize.define('UserTest', { username: DataTypes.STRING });
});
it('through Sequelize.sync()', function() {
it('through Sequelize.sync()', async function() {
this.spy.resetHistory();
return this.sequelize.sync({ force: true, logging: false }).then(() => {
expect(this.spy.notCalled).to.be.true;
});
await this.sequelize.sync({ force: true, logging: false });
expect(this.spy.notCalled).to.be.true;
});
it('through DAOFactory.sync()', function() {
it('through DAOFactory.sync()', async function() {
this.spy.resetHistory();
return this.User.sync({ force: true, logging: false }).then(() => {
expect(this.spy.notCalled).to.be.true;
});
await this.User.sync({ force: true, logging: false });
expect(this.spy.notCalled).to.be.true;
});
});
......@@ -1223,11 +1187,10 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
});
describe('drop should work', () => {
it('correctly succeeds', function() {
it('correctly succeeds', async function() {
const User = this.sequelize.define('Users', { username: DataTypes.STRING });
return User.sync({ force: true }).then(() => {
return User.drop();
});
await User.sync({ force: true });
await User.drop();
});
});
......@@ -1247,13 +1210,13 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
DataTypes.ENUM('scheduled', 'active', 'finished')
].forEach(status => {
describe('enum', () => {
beforeEach(function() {
beforeEach(async function() {
this.sequelize = Support.createSequelizeInstance({
typeValidation: true
});
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() {
......@@ -1264,25 +1227,24 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
}).to.throw(Error, 'Values for ENUM have not been defined.');
});
it('correctly stores values', function() {
return this.Review.create({ status: 'active' }).then(review => {
expect(review.status).to.equal('active');
});
it('correctly stores values', async function() {
const review = await this.Review.create({ status: 'active' });
expect(review.status).to.equal('active');
});
it('correctly loads values', function() {
return this.Review.create({ status: 'active' }).then(() => {
return this.Review.findAll().then(reviews => {
expect(reviews[0].status).to.equal('active');
});
});
it('correctly loads values', async function() {
await this.Review.create({ status: 'active' });
const reviews = await this.Review.findAll();
expect(reviews[0].status).to.equal('active');
});
it("doesn't save an instance if value is not in the range of enums", function() {
return this.Review.create({ status: 'fnord' }).catch(err => {
it("doesn't save an instance if value is not in the range of enums", async function() {
try {
await this.Review.create({ status: 'fnord' });
} catch (err) {
expect(err).to.be.instanceOf(Error);
expect(err.message).to.equal('"fnord" is not a valid choice in ["scheduled","active","finished"]');
});
}
});
});
});
......@@ -1294,18 +1256,17 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
{ id: { type: DataTypes.BIGINT, allowNull: false, primaryKey: true, autoIncrement: true } }
].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));
return Picture.sync({ force: true }).then(() => {
Object.keys(customAttributes).forEach(attribute => {
Object.keys(customAttributes[attribute]).forEach(option => {
const optionValue = customAttributes[attribute][option];
if (typeof optionValue === 'function' && optionValue() instanceof DataTypes.ABSTRACT) {
expect(Picture.rawAttributes[attribute][option] instanceof optionValue).to.be.ok;
} else {
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue);
}
});
await Picture.sync({ force: true });
Object.keys(customAttributes).forEach(attribute => {
Object.keys(customAttributes[attribute]).forEach(option => {
const optionValue = customAttributes[attribute][option];
if (typeof optionValue === 'function' && optionValue() instanceof DataTypes.ABSTRACT) {
expect(Picture.rawAttributes[attribute][option] instanceof optionValue).to.be.ok;
} else {
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue);
}
});
});
});
......@@ -1315,236 +1276,181 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (current.dialect.supports.transactions) {
describe('transaction', () => {
beforeEach(function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
this.sequelizeWithTransaction = sequelize;
});
beforeEach(async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelizeWithTransaction = sequelize;
});
it('is a transaction method available', () => {
expect(Support.Sequelize).to.respondTo('transaction');
});
it('passes a transaction object to the callback', function() {
return this.sequelizeWithTransaction.transaction().then(t => {
expect(t).to.be.instanceOf(Transaction);
});
it('passes a transaction object to the callback', async function() {
const t = await this.sequelizeWithTransaction.transaction();
expect(t).to.be.instanceOf(Transaction);
});
it('allows me to define a callback on the result', function() {
return this.sequelizeWithTransaction.transaction().then(t => {
return t.commit();
});
it('allows me to define a callback on the result', async function() {
const t = await this.sequelizeWithTransaction.transaction();
await t.commit();
});
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 count = transaction => {
const count = async transaction => {
const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] });
return this.sequelizeWithTransaction.query(sql, { plain: true, transaction }).then(result => {
return result.cnt;
});
const result = await this.sequelizeWithTransaction.query(sql, { plain: true, transaction });
return result.cnt;
};
return TransactionTest.sync({ force: true }).then(() => {
return this.sequelizeWithTransaction.transaction();
}).then(t1 => {
this.t1 = t1;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
}).then(() => {
return expect(count()).to.eventually.equal(0);
}).then(() => {
return expect(count(this.t1)).to.eventually.equal(1);
}).then(() => {
return this.t1.commit();
}).then(() => {
return expect(count()).to.eventually.equal(1);
});
await TransactionTest.sync({ force: true });
const t1 = await this.sequelizeWithTransaction.transaction();
this.t1 = t1;
await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
await expect(count()).to.eventually.equal(0);
await expect(count(this.t1)).to.eventually.equal(1);
await this.t1.commit();
await expect(count()).to.eventually.equal(1);
});
} 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 aliasesMapping = new Map([['_0', 'cnt']]);
const count = transaction => {
const count = async transaction => {
const sql = this.sequelizeWithTransaction.getQueryInterface().queryGenerator.selectQuery('TransactionTests', { attributes: [['count(*)', 'cnt']] });
return this.sequelizeWithTransaction.query(sql, { plain: true, transaction, aliasesMapping }).then(result => {
return parseInt(result.cnt, 10);
});
const result = await this.sequelizeWithTransaction.query(sql, { plain: true, transaction, aliasesMapping });
return parseInt(result.cnt, 10);
};
return TransactionTest.sync({ force: true }).then(() => {
return this.sequelizeWithTransaction.transaction();
}).then(t1 => {
this.t1 = t1;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
}).then(() => {
return this.sequelizeWithTransaction.transaction();
}).then(t2 => {
this.t2 = t2;
return this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('bar');`, { transaction: t2 });
}).then(() => {
return expect(count()).to.eventually.equal(0);
}).then(() => {
return expect(count(this.t1)).to.eventually.equal(1);
}).then(() => {
return expect(count(this.t2)).to.eventually.equal(1);
}).then(() => {
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);
});
await TransactionTest.sync({ force: true });
const t1 = await this.sequelizeWithTransaction.transaction();
this.t1 = t1;
await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('foo');`, { transaction: t1 });
const t2 = await this.sequelizeWithTransaction.transaction();
this.t2 = t2;
await this.sequelizeWithTransaction.query(`INSERT INTO ${qq('TransactionTests')} (${qq('name')}) VALUES ('bar');`, { transaction: t2 });
await expect(count()).to.eventually.equal(0);
await expect(count(this.t1)).to.eventually.equal(1);
await expect(count(this.t2)).to.eventually.equal(1);
await this.t2.rollback();
await expect(count()).to.eventually.equal(0);
await this.t1.commit();
await 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 });
return User.sync({ force: true }).then(() => {
return this.sequelizeWithTransaction.transaction().then(t1 => {
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => {
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => {
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => {
return t2.commit().then(() => {
return user.reload({ transaction: t1 }).then(newUser => {
expect(newUser.username).to.equal('bar');
return t1.commit();
});
});
});
});
});
});
});
await User.sync({ force: true });
const t1 = await this.sequelizeWithTransaction.transaction();
const user = await User.create({ username: 'foo' }, { transaction: t1 });
const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
await user.update({ username: 'bar' }, { transaction: t2 });
await t2.commit();
const newUser = await user.reload({ transaction: t1 });
expect(newUser.username).to.equal('bar');
await t1.commit();
});
describe('supports rolling back to savepoints', () => {
beforeEach(function() {
beforeEach(async function() {
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() {
return this.sequelizeWithTransaction.transaction().then(transaction => {
this.transaction = transaction;
return this.sequelizeWithTransaction.transaction({ transaction });
}).then(sp1 => {
this.sp1 = sp1;
return this.User.create({}, { transaction: this.transaction });
}).then(() => {
return this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
}).then(sp2 => {
this.sp2 = sp2;
return this.User.create({}, { transaction: this.transaction });
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(2);
return this.sp1.rollback();
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(0);
return this.transaction.rollback();
});
it('rolls back to the first savepoint, undoing everything', async function() {
const transaction = await this.sequelizeWithTransaction.transaction();
this.transaction = transaction;
const sp1 = await this.sequelizeWithTransaction.transaction({ transaction });
this.sp1 = sp1;
await this.User.create({}, { transaction: this.transaction });
const sp2 = await this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
this.sp2 = sp2;
await this.User.create({}, { transaction: this.transaction });
const users0 = await this.User.findAll({ transaction: this.transaction });
expect(users0).to.have.length(2);
await this.sp1.rollback();
const users = await this.User.findAll({ transaction: this.transaction });
expect(users).to.have.length(0);
await this.transaction.rollback();
});
it('rolls back to the most recent savepoint, only undoing recent changes', function() {
return this.sequelizeWithTransaction.transaction().then(transaction => {
this.transaction = transaction;
return this.sequelizeWithTransaction.transaction({ transaction });
}).then(sp1 => {
this.sp1 = sp1;
return this.User.create({}, { transaction: this.transaction });
}).then(() => {
return this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
}).then(sp2 => {
this.sp2 = sp2;
return this.User.create({}, { transaction: this.transaction });
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(2);
return this.sp2.rollback();
}).then(() => {
return this.User.findAll({ transaction: this.transaction });
}).then(users => {
expect(users).to.have.length(1);
return this.transaction.rollback();
});
it('rolls back to the most recent savepoint, only undoing recent changes', async function() {
const transaction = await this.sequelizeWithTransaction.transaction();
this.transaction = transaction;
const sp1 = await this.sequelizeWithTransaction.transaction({ transaction });
this.sp1 = sp1;
await this.User.create({}, { transaction: this.transaction });
const sp2 = await this.sequelizeWithTransaction.transaction({ transaction: this.transaction });
this.sp2 = sp2;
await this.User.create({}, { transaction: this.transaction });
const users0 = await this.User.findAll({ transaction: this.transaction });
expect(users0).to.have.length(2);
await this.sp2.rollback();
const users = await this.User.findAll({ transaction: this.transaction });
expect(users).to.have.length(1);
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 });
return User.sync({ force: true }).then(() => {
return this.sequelizeWithTransaction.transaction().then(t1 => {
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => {
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => {
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => {
return t2.rollback().then(() => {
return user.reload({ transaction: t1 }).then(newUser => {
expect(newUser.username).to.equal('foo');
return t1.commit();
});
});
});
});
});
});
});
await User.sync({ force: true });
const t1 = await this.sequelizeWithTransaction.transaction();
const user = await User.create({ username: 'foo' }, { transaction: t1 });
const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
await user.update({ username: 'bar' }, { transaction: t2 });
await t2.rollback();
const newUser = await user.reload({ transaction: t1 });
expect(newUser.username).to.equal('foo');
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 });
return User.sync({ force: true }).then(() => {
return this.sequelizeWithTransaction.transaction().then(t1 => {
return User.create({ username: 'foo' }, { transaction: t1 }).then(user => {
return this.sequelizeWithTransaction.transaction({ transaction: t1 }).then(t2 => {
return user.update({ username: 'bar' }, { transaction: t2 }).then(() => {
return t1.rollback().then(() => {
return User.findAll().then(users => {
expect(users.length).to.equal(0);
});
});
});
});
});
});
});
await User.sync({ force: true });
const t1 = await this.sequelizeWithTransaction.transaction();
const user = await User.create({ username: 'foo' }, { transaction: t1 });
const t2 = await this.sequelizeWithTransaction.transaction({ transaction: t1 });
await user.update({ username: 'bar' }, { transaction: t2 });
await t1.rollback();
const users = await User.findAll();
expect(users.length).to.equal(0);
});
});
}
});
describe('databaseVersion', () => {
it('should database/dialect version', function() {
return this.sequelize.databaseVersion().then(version => {
expect(typeof version).to.equal('string');
expect(version).to.be.ok;
});
it('should database/dialect version', async function() {
const version = await this.sequelize.databaseVersion();
expect(typeof version).to.equal('string');
expect(version).to.be.ok;
});
});
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),
epoch = Number(epochObj);
const User = this.sequelize.define('user', {
......@@ -1557,44 +1463,38 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
paranoid: true
});
return this.sequelize.sync({ force: true }).then(() => {
return User.create({ username: 'user1' }).then(user => {
expect(Number(user.deletedAt)).to.equal(epoch);
return User.findOne({
where: {
username: 'user1'
}
}).then(user => {
expect(user).to.exist;
expect(Number(user.deletedAt)).to.equal(epoch);
return user.destroy();
}).then(destroyedUser => {
expect(destroyedUser.deletedAt).to.exist;
expect(Number(destroyedUser.deletedAt)).not.to.equal(epoch);
return User.findByPk(destroyedUser.id, { paranoid: false });
}).then(fetchedDestroyedUser => {
expect(fetchedDestroyedUser.deletedAt).to.exist;
expect(Number(fetchedDestroyedUser.deletedAt)).not.to.equal(epoch);
return fetchedDestroyedUser.restore();
}).then(restoredUser => {
expect(Number(restoredUser.deletedAt)).to.equal(epoch);
return User.destroy({ where: {
username: 'user1'
} });
}).then(() => {
return User.count();
}).then(count => {
expect(count).to.equal(0);
return User.restore();
}).then(() => {
return User.findAll();
}).then(nonDeletedUsers => {
expect(nonDeletedUsers.length).to.equal(1);
nonDeletedUsers.forEach(u => {
expect(Number(u.deletedAt)).to.equal(epoch);
});
});
});
await this.sequelize.sync({ force: true });
const user = await User.create({ username: 'user1' });
expect(Number(user.deletedAt)).to.equal(epoch);
const user0 = await User.findOne({
where: {
username: 'user1'
}
});
expect(user0).to.exist;
expect(Number(user0.deletedAt)).to.equal(epoch);
const destroyedUser = await user0.destroy();
expect(destroyedUser.deletedAt).to.exist;
expect(Number(destroyedUser.deletedAt)).not.to.equal(epoch);
const fetchedDestroyedUser = await User.findByPk(destroyedUser.id, { paranoid: false });
expect(fetchedDestroyedUser.deletedAt).to.exist;
expect(Number(fetchedDestroyedUser.deletedAt)).not.to.equal(epoch);
const restoredUser = await fetchedDestroyedUser.restore();
expect(Number(restoredUser.deletedAt)).to.equal(epoch);
await User.destroy({ where: {
username: 'user1'
} });
const count = await User.count();
expect(count).to.equal(0);
await User.restore();
const nonDeletedUsers = await User.findAll();
expect(nonDeletedUsers.length).to.equal(1);
nonDeletedUsers.forEach(u => {
expect(Number(u.deletedAt)).to.equal(epoch);
});
});
});
......
......@@ -12,142 +12,126 @@ if (current.dialect.supports.transactions) {
describe(Support.getTestDialectTeaser('Sequelize#transaction'), () => {
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;
return this
const t = await this
.sequelize
.transaction().then(t => {
return t.commit().then(() => {
called = 1;
});
})
.then(() => {
expect(called).to.be.ok;
});
.transaction();
await t.commit();
called = 1;
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;
return this
const t = await this
.sequelize
.transaction().then(t => {
return t.rollback().then(() => {
called = 1;
});
})
.then(() => {
expect(called).to.be.ok;
});
.transaction();
await t.rollback();
called = 1;
expect(called).to.be.ok;
});
if (Support.getTestDialect() !== 'sqlite') {
it('works for long running transactions', function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
this.sequelize = sequelize;
this.User = sequelize.define('User', {
name: Support.Sequelize.STRING
}, { timestamps: false });
return sequelize.sync({ force: true });
}).then(() => {
return this.sequelize.transaction();
}).then(t => {
let query = 'select sleep(2);';
switch (Support.getTestDialect()) {
case 'postgres':
query = 'select pg_sleep(2);';
break;
case 'sqlite':
query = 'select sqlite3_sleep(2000);';
break;
case 'mssql':
query = 'WAITFOR DELAY \'00:00:02\';';
break;
default:
break;
}
return this.sequelize.query(query, { transaction: t }).then(() => {
return this.User.create({ name: 'foo' });
}).then(() => {
return this.sequelize.query(query, { transaction: t });
}).then(() => {
return t.commit();
});
}).then(() => {
return this.User.findAll();
}).then(users => {
expect(users.length).to.equal(1);
expect(users[0].name).to.equal('foo');
});
it('works for long running transactions', async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelize = sequelize;
this.User = sequelize.define('User', {
name: Support.Sequelize.STRING
}, { timestamps: false });
await sequelize.sync({ force: true });
const t = await this.sequelize.transaction();
let query = 'select sleep(2);';
switch (Support.getTestDialect()) {
case 'postgres':
query = 'select pg_sleep(2);';
break;
case 'sqlite':
query = 'select sqlite3_sleep(2000);';
break;
case 'mssql':
query = 'WAITFOR DELAY \'00:00:02\';';
break;
default:
break;
}
await this.sequelize.query(query, { transaction: t });
await this.User.create({ name: 'foo' });
await this.sequelize.query(query, { transaction: t });
await t.commit();
const users = await this.User.findAll();
expect(users.length).to.equal(1);
expect(users[0].name).to.equal('foo');
});
}
});
describe('complex long running example', () => {
it('works with promise syntax', function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
const Test = sequelize.define('Test', {
id: { type: Support.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: Support.Sequelize.STRING }
});
return sequelize.sync({ force: true }).then(() => {
return sequelize.transaction().then(transaction => {
expect(transaction).to.be.instanceOf(Transaction);
return Test
.create({ name: 'Peter' }, { transaction })
.then(() => {
return delay(1000).then(() => {
return transaction
.commit()
.then(() => { return Test.count(); })
.then(count => {
expect(count).to.equal(1);
});
});
});
});
});
it('works with promise syntax', async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
const Test = sequelize.define('Test', {
id: { type: Support.Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: Support.Sequelize.STRING }
});
await sequelize.sync({ force: true });
const transaction = await sequelize.transaction();
expect(transaction).to.be.instanceOf(Transaction);
await Test
.create({ name: 'Peter' }, { transaction });
await delay(1000);
await transaction
.commit();
const count = await Test.count();
expect(count).to.equal(1);
});
});
describe('concurrency', () => {
describe('having tables with uniqueness constraints', () => {
beforeEach(function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
this.sequelize = sequelize;
this.Model = sequelize.define('Model', {
name: { type: Support.Sequelize.STRING, unique: true }
}, {
timestamps: false
});
return this.Model.sync({ force: true });
beforeEach(async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
this.sequelize = sequelize;
this.Model = sequelize.define('Model', {
name: { type: Support.Sequelize.STRING, unique: true }
}, {
timestamps: false
});
await this.Model.sync({ force: true });
});
it('triggers the error event for the second transactions', function() {
return this.sequelize.transaction().then(t1 => {
return this.sequelize.transaction().then(t2 => {
return this.Model.create({ name: 'omnom' }, { transaction: t1 }).then(() => {
return Promise.all([
this.Model.create({ name: 'omnom' }, { transaction: t2 }).catch(err => {
expect(err).to.be.ok;
return t2.rollback();
}),
delay(100).then(() => {
return t1.commit();
})
]);
});
});
});
it('triggers the error event for the second transactions', async function() {
const t1 = await this.sequelize.transaction();
const t2 = await this.sequelize.transaction();
await this.Model.create({ name: 'omnom' }, { transaction: t1 });
await Promise.all([
(async () => {
try {
return await this.Model.create({ name: 'omnom' }, { transaction: t2 });
} catch (err) {
expect(err).to.be.ok;
return t2.rollback();
}
})(),
delay(100).then(() => {
return t1.commit();
})
]);
});
});
});
......
......@@ -13,8 +13,8 @@ before(function() {
});
});
beforeEach(function() {
return Support.clearDatabase(this.sequelize);
beforeEach(async function() {
await Support.clearDatabase(this.sequelize);
});
afterEach(function() {
......
......@@ -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()';
const startQueryTime = Date.now();
......@@ -27,48 +27,45 @@ if (dialect !== 'sqlite') {
}
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.sequelizeWithTimezone.query(query, { type: this.sequelize.QueryTypes.SELECT })
]).then(([now1, now2]) => {
const elapsedQueryTime = Date.now() - startQueryTime + 1001;
expect(now1[0].now.getTime()).to.be.closeTo(now2[0].now.getTime(), elapsedQueryTime);
});
]);
const elapsedQueryTime = Date.now() - startQueryTime + 1001;
expect(now1[0].now.getTime()).to.be.closeTo(now2[0].now.getTime(), elapsedQueryTime);
});
if (dialect === 'mysql' || dialect === 'mariadb') {
it('handles existing timestamps', function() {
it('handles existing timestamps', async function() {
const NormalUser = this.sequelize.define('user', {}),
TimezonedUser = this.sequelizeWithTimezone.define('user', {});
return this.sequelize.sync({ force: true }).then(() => {
return NormalUser.create({});
}).then(normalUser => {
this.normalUser = normalUser;
return TimezonedUser.findByPk(normalUser.id);
}).then(timezonedUser => {
// 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 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);
});
await this.sequelize.sync({ force: true });
const normalUser = await NormalUser.create({});
this.normalUser = normalUser;
const timezonedUser = await TimezonedUser.findByPk(normalUser.id);
// 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 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);
});
it('handles named timezones', function() {
it('handles named timezones', async function() {
const NormalUser = this.sequelize.define('user', {}),
TimezonedUser = this.sequelizeWithNamedTimezone.define('user', {});
return this.sequelize.sync({ force: true }).then(() => {
return TimezonedUser.create({});
}).then(timezonedUser => {
return Promise.all([
NormalUser.findByPk(timezonedUser.id),
TimezonedUser.findByPk(timezonedUser.id)
]);
}).then(([normalUser, timezonedUser]) => {
// 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);
});
await this.sequelize.sync({ force: true });
const timezonedUser0 = await TimezonedUser.create({});
const [normalUser, timezonedUser] = await Promise.all([
NormalUser.findByPk(timezonedUser0.id),
TimezonedUser.findByPk(timezonedUser0.id)
]);
// 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);
});
}
});
......
......@@ -55,55 +55,59 @@ if (current.dialect.supports.transactions) {
});
describe('autoCallback', () => {
it('supports automatically committing', function() {
return this.sequelize.transaction(() => {
return Promise.resolve();
});
it('supports automatically committing', async function() {
await this.sequelize.transaction(async () => {});
});
it('supports automatically rolling back with a thrown error', function() {
it('supports automatically rolling back with a thrown error', async function() {
let t;
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(transaction => {
t = transaction;
throw new Error('Yolo');
})).to.eventually.be.rejected.then(() => {
expect(t.finished).to.be.equal('rollback');
});
})).to.eventually.be.rejected;
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;
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(async transaction => {
t = transaction;
return Promise.reject(new Error('Swag'));
})).to.eventually.be.rejected.then(() => {
expect(t.finished).to.be.equal('rollback');
});
throw new Error('Swag');
})).to.eventually.be.rejected;
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();
let transaction;
return expect(this.sequelize.transaction(t => {
transaction = t;
transaction.afterCommit(hook);
return this.sequelize.query('SELECT 1+1', { transaction, type: QueryTypes.SELECT });
}).then(() => {
await expect((async () => {
await this.sequelize.transaction(t => {
transaction = t;
transaction.afterCommit(hook);
return this.sequelize.query('SELECT 1+1', { transaction, type: QueryTypes.SELECT });
});
expect(hook).to.have.been.calledOnce;
expect(hook).to.have.been.calledWith(transaction);
})
})()
).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();
return expect(this.sequelize.transaction(transaction => {
await expect(this.sequelize.transaction(async transaction => {
transaction.afterCommit(hook);
return Promise.reject(new Error('Rollback'));
throw new Error('Rollback');
})
).to.eventually.be.rejected.then(() => {
expect(hook).to.not.have.been.called;
});
).to.eventually.be.rejected;
expect(hook).to.not.have.been.called;
});
//Promise rejection test is specific to postgres
......@@ -152,7 +156,8 @@ if (current.dialect.supports.transactions) {
});
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 Promise.all([
expect(t.commit()).to.eventually.be.fulfilled,
......@@ -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\)/
).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() {
return expect(
this.sequelize.transaction().then(t => {
return this.sequelize.query('SELECT 1+1', { transaction: t, raw: true }).then(() => {
return t.rollback();
}).then(() => {
return this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
});
})
it('does not allow queries after rollback', async function() {
await expect(
(async () => {
const t = await this.sequelize.transaction();
await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
await t.rollback();
return await this.sequelize.query('SELECT 1+1', { transaction: t, raw: true });
})()
).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')
.returns(new Promise(() => {}));
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');
});
......@@ -200,174 +204,184 @@ if (current.dialect.supports.transactions) {
).to.eventually.be.fulfilled;
});
it('does not allow commits after commit', function() {
return expect(
this.sequelize.transaction().then(t => {
return t.commit().then(() => {
return t.commit();
});
})
it('does not allow commits after commit', async function() {
await expect(
(async () => {
const t = await this.sequelize.transaction();
await t.commit();
return await t.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();
let transaction;
return expect(
this.sequelize.transaction().then(t => {
transaction = t;
transaction.afterCommit(hook);
return t.commit().then(() => {
await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t;
transaction.afterCommit(hook);
await t.commit();
expect(hook).to.have.been.calledOnce;
expect(hook).to.have.been.calledWith(t);
});
}).catch(err => {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
return transaction.rollback().then(() => {
} catch (err) {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
await transaction.rollback();
throw err;
});
}
throw err;
}
throw err;
})
})()
).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();
return expect(
this.sequelize.transaction().then(t => {
await expect(
(async () => {
const t = await this.sequelize.transaction();
t.afterCommit(hook);
return t.rollback().then(() => {
expect(hook).to.not.have.been.called;
});
})
await t.rollback();
expect(hook).to.not.have.been.called;
})()
).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;
let transaction;
return expect(
this.sequelize.transaction().then(t => {
transaction = t;
transaction.afterCommit(hook);
return t.commit();
}).catch(err => {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
return transaction.rollback().then(() => {
await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t;
transaction.afterCommit(hook);
return await t.commit();
} catch (err) {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
await transaction.rollback();
throw err;
});
}
throw err;
}
throw err;
})
})()
).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;
let transaction;
return expect(
this.sequelize.transaction().then(t => {
transaction = t;
transaction.afterCommit(hook);
return t.commit();
}).catch(err => {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
return transaction.rollback().then(() => {
await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t;
transaction.afterCommit(hook);
return await t.commit();
} catch (err) {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
await transaction.rollback();
throw err;
});
}
throw err;
}
throw err;
})
})()
).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 = {};
let transaction;
return expect(
this.sequelize.transaction().then(t => {
transaction = t;
transaction.afterCommit(hook);
return t.commit();
}).catch(err => {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
return transaction.rollback().then(() => {
await expect(
(async () => {
try {
const t = await this.sequelize.transaction();
transaction = t;
transaction.afterCommit(hook);
return await t.commit();
} catch (err) {
// Cleanup this transaction so other tests don't
// fail due to an open transaction
if (!transaction.finished) {
await transaction.rollback();
throw err;
});
}
throw err;
}
throw err;
})
})()
).to.eventually.be.rejectedWith('"fn" must be a function');
});
it('does not allow commits after rollback', function() {
return expect(this.sequelize.transaction().then(t => {
return t.rollback().then(() => {
return t.commit();
});
})).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: rollback');
it('does not allow commits after rollback', async function() {
await expect((async () => {
const t = await this.sequelize.transaction();
await t.rollback();
return await t.commit();
})()).to.be.rejectedWith('Transaction cannot be committed because it has been finished with state: rollback');
});
it('does not allow rollbacks after commit', function() {
return expect(this.sequelize.transaction().then(t => {
return t.commit().then(() => {
return t.rollback();
});
})).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: commit');
it('does not allow rollbacks after commit', async function() {
await expect((async () => {
const t = await this.sequelize.transaction();
await t.commit();
return await t.rollback();
})()).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: commit');
});
it('does not allow rollbacks after rollback', function() {
return expect(this.sequelize.transaction().then(t => {
return t.rollback().then(() => {
return t.rollback();
});
})).to.be.rejectedWith('Transaction cannot be rolled back because it has been finished with state: rollback');
it('does not allow rollbacks after rollback', async function() {
await expect((async () => {
const t = await this.sequelize.transaction();
await t.rollback();
return await t.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');
return this.sequelize.transaction({
const t = await this.sequelize.transaction({
transaction: null
}).then(t => {
return t.commit().then(() => {
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
}
});
});
await t.commit();
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) {
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');
return this.sequelize.transaction({
const t = await this.sequelize.transaction({
transaction: undefined
}).then(t => {
return t.commit().then(() => {
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
}
});
});
await t.commit();
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (let i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
}
});
if (dialect === 'mysql' || dialect === 'mariadb') {
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', {
id: {
type: Sequelize.INTEGER,
......@@ -382,19 +396,22 @@ if (current.dialect.supports.transactions) {
// execute a query, we expect the newly-created rows to be destroyed when we forcibly rollback by
// throwing an error.
// 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 => {
return Task.findAll({
where: {
id: {
[Sequelize.Op.eq]: from
}
},
lock: 'UPDATE',
transaction
})
.then(() => delay(10))
.then(() => {
return Task.update({ id: to }, {
const update = async (from, to) => this.sequelize.transaction(async transaction => {
try {
try {
await Task.findAll({
where: {
id: {
[Sequelize.Op.eq]: from
}
},
lock: 'UPDATE',
transaction
});
await delay(10);
await Task.update({ id: to }, {
where: {
id: {
[Sequelize.Op.ne]: to
......@@ -403,80 +420,73 @@ if (current.dialect.supports.transactions) {
lock: transaction.LOCK.UPDATE,
transaction
});
})
.catch(e => { console.log(e.message); })
.then(() => Task.create({ id: 2 }, { transaction }))
.catch(e => { console.log(e.message); })
.then(() => { throw new Error('Rollback!'); });
} catch (e) {
console.log(e.message);
}
await Task.create({ id: 2 }, { transaction });
} catch (e) {
console.log(e.message);
}
throw new Error('Rollback!');
}).catch(() => {});
return this.sequelize.sync({ force: true })
.then(() => Task.create({ id: 0 }))
.then(() => Task.create({ id: 1 }))
.then(() => Promise.all([
update(1, 0),
update(0, 1)
]))
.then(() => {
return Task.count().then(count => {
// 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');
});
});
await this.sequelize.sync({ force: true });
await Task.create({ id: 0 });
await Task.create({ id: 1 });
await Promise.all([
update(1, 0),
update(0, 1)
]);
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.
expect(count).to.equal(2, 'transactions were fully rolled-back, and no new rows were added');
});
});
}
if (dialect === 'sqlite') {
it('provides persistent transactions', () => {
it('provides persistent transactions', async () => {
const sequelize = new Support.Sequelize('database', 'username', 'password', { dialect: 'sqlite' }),
User = sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
});
let persistentTransaction;
return sequelize.transaction().then(t => {
return sequelize.sync({ transaction: t }).then(( ) => {
return t;
});
}).then(t => {
return User.create({}, { transaction: t }).then(( ) => {
return t.commit();
});
}).then(() => {
return sequelize.transaction().then(t => {
persistentTransaction = t;
});
}).then(() => {
return User.findAll({ transaction: persistentTransaction }).then(users => {
expect(users.length).to.equal(1);
return persistentTransaction.commit();
});
});
const t1 = await sequelize.transaction();
await sequelize.sync({ transaction: t1 });
const t0 = t1;
await User.create({}, { transaction: t0 });
await t0.commit();
const persistentTransaction = await sequelize.transaction();
const users = await User.findAll({ transaction: persistentTransaction });
expect(users.length).to.equal(1);
await persistentTransaction.commit();
});
}
if (current.dialect.supports.transactionOptions.type) {
describe('transaction types', () => {
it('should support default transaction type DEFERRED', function() {
return this.sequelize.transaction({
}).then(t => {
return t.rollback().then(() => {
expect(t.options.type).to.equal('DEFERRED');
});
it('should support default transaction type DEFERRED', async function() {
const t = await this.sequelize.transaction({
});
await t.rollback();
expect(t.options.type).to.equal('DEFERRED');
});
Object.keys(Transaction.TYPES).forEach(key => {
it(`should allow specification of ${key} type`, function() {
return this.sequelize.transaction({
it(`should allow specification of ${key} type`, async function() {
const t = await this.sequelize.transaction({
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) {
}
if (dialect === 'sqlite') {
it('automatically retries on SQLITE_BUSY failure', function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
const User = sequelize.define('User', { username: Support.Sequelize.STRING });
return User.sync({ force: true }).then(() => {
const newTransactionFunc = function() {
return sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE }).then(t => {
return User.create({}, { transaction: t }).then(( ) => {
return t.commit();
});
});
};
return Promise.all([newTransactionFunc(), newTransactionFunc()]).then(() => {
return User.findAll().then(users => {
expect(users.length).to.equal(2);
});
});
});
});
it('automatically retries on SQLITE_BUSY failure', async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { username: Support.Sequelize.STRING });
await User.sync({ force: true });
const newTransactionFunc = async function() {
const t = await sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE });
await User.create({}, { transaction: t });
return t.commit();
};
await Promise.all([newTransactionFunc(), newTransactionFunc()]);
const users = await User.findAll();
expect(users.length).to.equal(2);
});
it('fails with SQLITE_BUSY when retry.match is changed', function() {
return Support.prepareTransactionTest(this.sequelize).then(sequelize => {
const User = sequelize.define('User', { id: { type: Support.Sequelize.INTEGER, primaryKey: true }, username: Support.Sequelize.STRING });
return User.sync({ force: true }).then(() => {
const newTransactionFunc = function() {
return sequelize.transaction({ type: Support.Sequelize.Transaction.TYPES.EXCLUSIVE, retry: { match: ['NO_MATCH'] } }).then(t => {
// introduce delay to force the busy state race condition to fail
return delay(1000).then(() => {
return User.create({ id: null, username: `test ${t.id}` }, { transaction: t }).then(() => {
return t.commit();
});
});
});
};
return expect(Promise.all([newTransactionFunc(), newTransactionFunc()])).to.be.rejectedWith('SQLITE_BUSY: database is locked');
});
});
it('fails with SQLITE_BUSY when retry.match is changed', async function() {
const sequelize = await Support.prepareTransactionTest(this.sequelize);
const User = sequelize.define('User', { id: { type: Support.Sequelize.INTEGER, primaryKey: true }, username: Support.Sequelize.STRING });
await User.sync({ force: true });
const newTransactionFunc = async function() {
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
await delay(1000);
await User.create({ id: null, username: `test ${t.id}` }, { transaction: t });
return t.commit();
};
await expect(Promise.all([newTransactionFunc(), newTransactionFunc()])).to.be.rejectedWith('SQLITE_BUSY: database is locked');
});
}
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', {
username: Support.Sequelize.STRING
});
return expect(
await expect(
this.sequelize.sync({ force: true }).then(() => {
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }, transaction => {
return User.findAll({ transaction })
.then(users => expect( users ).to.have.lengthOf(0))
.then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction
.then(() => User.findAll({ transaction }))
.then(users => expect( users ).to.have.lengthOf(1)); // We SHOULD see the created user inside the transaction
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async transaction => {
const users0 = await User.findAll({ transaction });
await expect( users0 ).to.have.lengthOf(0);
await User.create({ username: 'jan' }); // Create a User outside of the transaction
const users = await User.findAll({ transaction });
return expect( users ).to.have.lengthOf(1); // We SHOULD see the created user inside the transaction
});
})
).to.eventually.be.fulfilled;
......@@ -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
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', {
username: Support.Sequelize.STRING
});
return expect(
await expect(
this.sequelize.sync({ force: true }).then(() => {
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ }, transaction => {
return User.findAll({ transaction })
.then(users => expect( users ).to.have.lengthOf(0))
.then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction
.then(() => User.findAll({ transaction }))
.then(users => expect( users ).to.have.lengthOf(0)); // We SHOULD NOT see the created user inside the transaction
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ }, async transaction => {
const users0 = await User.findAll({ transaction });
await expect( users0 ).to.have.lengthOf(0);
await User.create({ username: 'jan' }); // Create a User outside of the transaction
const users = await User.findAll({ transaction });
return expect( users ).to.have.lengthOf(0); // We SHOULD NOT see the created user inside the transaction
});
})
).to.eventually.be.fulfilled;
......@@ -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
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', {
username: Support.Sequelize.STRING
}),
transactionSpy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => {
return User.create({ username: 'jan' });
}).then(() => {
return this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE }).then(transaction => {
return User.findAll( { transaction } )
.then(() => Promise.all([// Update should not succeed before transaction has committed
User.update({ username: 'joe' }, {
where: {
username: 'jan'
}
}).then(() => expect(transactionSpy).to.have.been.called ), delay(2000)
.then(() => transaction.commit())
.then(transactionSpy)]));
});
});
await this.sequelize.sync({ force: true });
await User.create({ username: 'jan' });
const transaction = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE });
await User.findAll( { transaction } );
await Promise.all([// Update should not succeed before transaction has committed
User.update({ username: 'joe' }, {
where: {
username: 'jan'
}
}).then(() => expect(transactionSpy).to.have.been.called ), delay(2000)
.then(() => transaction.commit())
.then(transactionSpy)]);
});
}
......@@ -597,7 +594,7 @@ if (current.dialect.supports.transactions) {
if (current.dialect.supports.lock) {
describe('row locking', () => {
it('supports for update', function() {
it('supports for update', async function() {
const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
......@@ -605,186 +602,185 @@ if (current.dialect.supports.transactions) {
t1Spy = sinon.spy(),
t2Spy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => {
return User.create({ username: 'jan' });
}).then(() => {
return this.sequelize.transaction().then(t1 => {
return User.findOne({
where: {
username: 'jan'
},
lock: t1.LOCK.UPDATE,
transaction: t1
}).then(t1Jan => {
return this.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
}).then(t2 => {
return Promise.all([User.findOne({
where: {
username: 'jan'
},
lock: t2.LOCK.UPDATE,
transaction: t2
}).then(() => {
t2Spy();
return t2.commit().then(() => {
expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has committed
});
}), t1Jan.update({
awesome: true
}, {
transaction: t1
}).then(() => {
t1Spy();
return delay(2000).then(() => {
return t1.commit();
});
})]);
});
});
});
await this.sequelize.sync({ force: true });
await User.create({ username: 'jan' });
const t1 = await this.sequelize.transaction();
const t1Jan = await User.findOne({
where: {
username: 'jan'
},
lock: t1.LOCK.UPDATE,
transaction: t1
});
const t2 = await this.sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED
});
await Promise.all([(async () => {
await User.findOne({
where: {
username: 'jan'
},
lock: t2.LOCK.UPDATE,
transaction: t2
});
t2Spy();
await t2.commit();
expect(t2Spy).to.have.been.calledAfter(t1Spy); // Find should not succeed before t1 has committed
})(), (async () => {
await t1Jan.update({
awesome: true
}, {
transaction: t1
});
t1Spy();
await delay(2000);
return await t1.commit();
})()]);
});
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', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
});
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create(
{ username: 'jan' }
),
User.create(
{ username: 'joe' }
)
]);
}).then(() => {
return this.sequelize.transaction().then(t1 => {
return User.findAll({
limit: 1,
lock: true,
transaction: t1
}).then(results => {
const firstUserId = results[0].id;
return this.sequelize.transaction().then(t2 => {
return User.findAll({
limit: 1,
lock: true,
skipLocked: true,
transaction: t2
}).then(secondResults => {
expect(secondResults[0].id).to.not.equal(firstUserId);
return Promise.all([
t1.commit(),
t2.commit()
]);
});
});
});
});
await this.sequelize.sync({ force: true });
await Promise.all([
User.create(
{ username: 'jan' }
),
User.create(
{ username: 'joe' }
)
]);
const t1 = await this.sequelize.transaction();
const results = await User.findAll({
limit: 1,
lock: true,
transaction: t1
});
const firstUserId = results[0].id;
const t2 = await this.sequelize.transaction();
const secondResults = await User.findAll({
limit: 1,
lock: true,
skipLocked: true,
transaction: t2
});
expect(secondResults[0].id).to.not.equal(firstUserId);
await Promise.all([
t1.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 }),
Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN });
User.belongsToMany(Task, { through: 'UserTasks' });
Task.belongsToMany(User, { through: 'UserTasks' });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false })
]).then(([john, task1]) => {
return john.setTasks([task1]);
})
.then(() => {
return this.sequelize.transaction(t1 => {
if (current.dialect.supports.lockOuterJoinFailure) {
return expect(User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
})).to.be.rejectedWith('FOR UPDATE cannot be applied to the nullable side of an outer join');
}
return User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
});
});
});
await this.sequelize.sync({ force: true });
const [john, task1] = await Promise.all([
User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false })
]);
await john.setTasks([task1]);
await this.sequelize.transaction(t1 => {
if (current.dialect.supports.lockOuterJoinFailure) {
return expect(User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
})).to.be.rejectedWith('FOR UPDATE cannot be applied to the nullable side of an outer join');
}
return User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: t1.LOCK.UPDATE,
transaction: t1
});
});
});
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' }),
Task = this.sequelize.define('Task', { title: Support.Sequelize.STRING, active: Support.Sequelize.BOOLEAN });
User.belongsToMany(Task, { through: 'UserTasks' });
Task.belongsToMany(User, { through: 'UserTasks' });
return this.sequelize.sync({ force: true }).then(() => {
return Promise.all([
User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false }),
Task.create({ title: 'Die trying', active: false })
]).then(([john, task1]) => {
return john.setTasks([task1]);
})
.then(() => {
return this.sequelize.transaction(t1 => {
return User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: {
level: t1.LOCK.UPDATE,
of: User
},
transaction: t1
}).then(t1John => {
// should not be blocked by the lock of the other transaction
return this.sequelize.transaction(t2 => {
return Task.update({
active: true
}, {
where: {
active: false
},
transaction: t2
});
}).then(() => {
return t1John.save({
transaction: t1
});
});
});
});
await this.sequelize.sync({ force: true });
const [john, task1] = await Promise.all([
User.create({ username: 'John' }),
Task.create({ title: 'Get rich', active: false }),
Task.create({ title: 'Die trying', active: false })
]);
await john.setTasks([task1]);
await this.sequelize.transaction(async t1 => {
const t1John = await User.findOne({
where: {
username: 'John'
},
include: [Task],
lock: {
level: t1.LOCK.UPDATE,
of: User
},
transaction: t1
});
// should not be blocked by the lock of the other transaction
await this.sequelize.transaction(t2 => {
return Task.update({
active: true
}, {
where: {
active: false
},
transaction: t2
});
});
return t1John.save({
transaction: t1
});
});
});
}
if (current.dialect.supports.lockKey) {
it('supports for key share', function() {
it('supports for key share', async function() {
const User = this.sequelize.define('user', {
username: Support.Sequelize.STRING,
awesome: Support.Sequelize.BOOLEAN
......@@ -792,42 +788,43 @@ if (current.dialect.supports.transactions) {
t1Spy = sinon.spy(),
t2Spy = sinon.spy();
return this.sequelize.sync({ force: true }).then(() => {
return User.create({ username: 'jan' });
}).then(() => {
return this.sequelize.transaction().then(t1 => {
return User.findOne({
where: {
username: 'jan'
},
lock: t1.LOCK.NO_KEY_UPDATE,
transaction: t1
}).then(t1Jan => {
return this.sequelize.transaction().then(t2 => {
return Promise.all([User.findOne({
where: {
username: 'jan'
},
lock: t2.LOCK.KEY_SHARE,
transaction: t2
}).then(() => {
t2Spy();
return t2.commit();
}), t1Jan.update({
awesome: true
}, {
transaction: t1
}).then(() => {
return delay(2000).then(() => {
t1Spy();
expect(t1Spy).to.have.been.calledAfter(t2Spy);
return t1.commit();
});
})]);
});
});
});
await this.sequelize.sync({ force: true });
await User.create({ username: 'jan' });
const t1 = await this.sequelize.transaction();
const t1Jan = await User.findOne({
where: {
username: 'jan'
},
lock: t1.LOCK.NO_KEY_UPDATE,
transaction: t1
});
const t2 = await this.sequelize.transaction();
await Promise.all([(async () => {
await User.findOne({
where: {
username: 'jan'
},
lock: t2.LOCK.KEY_SHARE,
transaction: t2
});
t2Spy();
return await t2.commit();
})(), (async () => {
await t1Jan.update({
awesome: true
}, {
transaction: t1
});
await delay(2000);
t1Spy();
expect(t1Spy).to.have.been.calledAfter(t2Spy);
return await t1.commit();
})()]);
});
}
......@@ -855,21 +852,24 @@ if (current.dialect.supports.transactions) {
});
await Promise.all([
User.findByPk(user.id, {
transaction: t2
}).then(async t2Jan => {
(async () => {
const t2Jan = await User.findByPk(user.id, {
transaction: t2
});
t2FindSpy();
await t2Jan.update({ awesome: false }, { transaction: t2 });
t2UpdateSpy();
await t2.commit();
}),
t1Jan.update({ awesome: true }, { transaction: t1 }).then(async () => {
})(),
(async () => {
await t1Jan.update({ awesome: true }, { transaction: t1 });
await delay(2000);
t1CommitSpy();
await t1.commit();
})
})()
]);
// (t2) find call should have returned before (t1) commit
......
......@@ -22,7 +22,7 @@ if (current.dialect.supports.tmpTableTrigger) {
'select * from deleted\n' +
'end\n';
beforeEach(function() {
beforeEach(async function() {
User = this.sequelize.define('user', {
username: {
type: Sequelize.STRING,
......@@ -32,56 +32,52 @@ if (current.dialect.supports.tmpTableTrigger) {
hasTrigger: true
});
return User.sync({ force: true }).then(() => {
return this.sequelize.query(triggerQuery, { type: this.sequelize.QueryTypes.RAW });
});
await User.sync({ force: true });
await this.sequelize.query(triggerQuery, { type: this.sequelize.QueryTypes.RAW });
});
it('should return output rows after insert', () => {
return User.create({
it('should return output rows after insert', async () => {
await User.create({
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', () => {
return User.create({
it('should return output rows after instance update', async () => {
const user = await User.create({
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', () => {
return User.create({
it('should return output rows after Model update', async () => {
const user = await User.create({
username: 'triggertest'
}).then(user => {
return User.update({
username: 'usernamechanged'
}, {
where: {
id: user.get('id')
}
});
})
.then(() => {
return expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
});
});
await User.update({
username: 'usernamechanged'
}, {
where: {
id: user.get('id')
}
});
await expect(User.findOne({ username: 'usernamechanged' })).to.eventually.have.property('username').which.equals('usernamechanged');
});
it('should successfully delete with a trigger on the table', () => {
return User.create({
it('should successfully delete with a trigger on the table', async () => {
const user = await User.create({
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,33 +138,33 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
describe('Sequelize.fn', () => {
let Airplane;
beforeEach(function() {
beforeEach(async function() {
Airplane = this.sequelize.define('Airplane', {
wings: DataTypes.INTEGER,
engines: DataTypes.INTEGER
});
return Airplane.sync({ force: true }).then(() => {
return Airplane.bulkCreate([
{
wings: 2,
engines: 0
}, {
wings: 4,
engines: 1
}, {
wings: 2,
engines: 2
}
]);
});
await Airplane.sync({ force: true });
await Airplane.bulkCreate([
{
wings: 2,
engines: 0
}, {
wings: 4,
engines: 1
}, {
wings: 2,
engines: 2
}
]);
});
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';
return Airplane.findAll({
const [airplane] = await Airplane.findAll({
attributes: [
[this.sequelize.fn('COUNT', '*'), 'count'],
[Sequelize.fn('SUM', Sequelize.cast({
......@@ -179,18 +179,18 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
}
}, type)), 'count-engines-wings']
]
}).then(([airplane]) => {
// TODO: `parseInt` should not be needed, see #10533
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-wings'), 10)).to.equal(2);
});
// TODO: `parseInt` should not be needed, see #10533
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-wings'), 10)).to.equal(2);
});
}
if (Support.getTestDialect() !== 'mssql' && Support.getTestDialect() !== 'postgres') {
it('accepts condition object (auto casting)', function() {
return Airplane.findAll({
it('accepts condition object (auto casting)', async function() {
const [airplane] = await Airplane.findAll({
attributes: [
[this.sequelize.fn('COUNT', '*'), 'count'],
[Sequelize.fn('SUM', {
......@@ -205,12 +205,12 @@ describe(Support.getTestDialectTeaser('Utils'), () => {
}
}), 'count-engines-wings']
]
}).then(([airplane]) => {
// TODO: `parseInt` should not be needed, see #10533
expect(airplane.get('count')).to.equal(3);
expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1);
expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2);
});
// TODO: `parseInt` should not be needed, see #10533
expect(airplane.get('count')).to.equal(3);
expect(parseInt(airplane.get('count-engines'), 10)).to.equal(1);
expect(parseInt(airplane.get('count-engines-wings'), 10)).to.equal(2);
});
}
});
......
......@@ -8,22 +8,21 @@ const chai = require('chai'),
chai.should();
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', {
name: Sequelize.STRING
}, {
tableName: 'student'
});
return Student.sync({ force: true }).then(() => {
return Student.create({
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"; --');
});
await Student.sync({ force: true });
const result0 = await Student.create({
name: '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!