123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- 'use strict';
- const chai = require('chai');
- const expect = chai.expect;
- const Support = require('./support');
- const { DataTypes, sql } = require('@sequelize/core');
- const dialect = Support.getTestDialect();
- const sinon = require('sinon');
- const isUUID = require('validator').isUUID;
- describe(Support.getTestDialectTeaser('Instance'), () => {
- before(function () {
- this.clock = sinon.useFakeTimers();
- });
- afterEach(function () {
- this.clock.reset();
- });
- after(function () {
- this.clock.restore();
- });
- beforeEach(async function () {
- this.User = this.sequelize.define('User', {
- username: { type: DataTypes.STRING },
- uuidv1: { type: DataTypes.UUID, defaultValue: sql.uuidV1 },
- uuidv4: { type: DataTypes.UUID, defaultValue: sql.uuidV4 },
- touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
- aNumber: { type: DataTypes.INTEGER },
- bNumber: { type: DataTypes.INTEGER },
- aDate: { type: DataTypes.DATE },
- validateTest: {
- type: DataTypes.INTEGER,
- allowNull: true,
- validate: { isInt: true },
- },
- validateCustom: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: { len: { msg: 'Length failed.', args: [1, 20] } },
- },
- dateAllowNullTrue: {
- type: DataTypes.DATE,
- allowNull: true,
- },
- isSuperUser: {
- type: DataTypes.BOOLEAN,
- defaultValue: false,
- },
- });
- await this.User.sync({ force: true });
- });
- describe('Escaping', () => {
- 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
- const u1 = await this.User.create({ username: bio });
- const u2 = await this.User.findByPk(u1.id);
- expect(u2.username).to.equal(bio);
- });
- });
- describe('isNewRecord', () => {
- it('returns true for non-saved objects', function () {
- const user = this.User.build({ username: 'user' });
- expect(user.id).to.be.null;
- expect(user.isNewRecord).to.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', async function () {
- const user = await this.User.create({ username: 'user' });
- expect(user.isNewRecord).to.not.be.ok;
- });
- it('returns false for upserted objects', async function () {
- if (!Support.sequelize.dialect.supports.upserts) {
- return;
- }
- // adding id here so MSSQL doesn't fail. It needs a primary key to upsert
- const [user] = await this.User.upsert({ id: 2, username: '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', async function () {
- const users = [];
- for (let i = 0; i < 10; i++) {
- users[i] = { username: 'user' };
- }
- await this.User.bulkCreate(users);
- const users0 = await this.User.findAll();
- for (const u of users0) {
- expect(u.isNewRecord).to.not.be.ok;
- }
- });
- });
- describe('default values', () => {
- describe('uuid', () => {
- it('should store a string in uuidv1 and uuidv4', function () {
- const user = this.User.build({ username: 'a user' });
- expect(user.uuidv1).to.be.a('string');
- expect(user.uuidv4).to.be.a('string');
- });
- it('should store a string of length 36 in uuidv1 and uuidv4', function () {
- const user = this.User.build({ username: 'a user' });
- expect(user.uuidv1).to.have.length(36);
- expect(user.uuidv4).to.have.length(36);
- });
- it('should store a valid uuid in uuidv1 and uuidv4 that conforms to the UUID v1 and v4 specifications', function () {
- const user = this.User.build({ username: 'a user' });
- expect(isUUID(user.uuidv1)).to.be.true;
- expect(isUUID(user.uuidv4, 4)).to.be.true;
- });
- it('should store a valid uuid if the multiple primary key fields used', function () {
- const Person = this.sequelize.define('Person', {
- id1: {
- type: DataTypes.UUID,
- defaultValue: sql.uuidV1,
- primaryKey: true,
- },
- id2: {
- type: DataTypes.UUID,
- defaultValue: sql.uuidV4,
- primaryKey: true,
- },
- });
- const person = Person.build({});
- expect(person.id1).to.be.ok;
- expect(person.id1).to.have.length(36);
- expect(person.id2).to.be.ok;
- expect(person.id2).to.have.length(36);
- });
- });
- describe('current date', () => {
- it('should store a date in touchedAt', function () {
- const user = this.User.build({ username: 'a user' });
- expect(user.touchedAt).to.be.instanceof(Date);
- });
- it('should store the current date in touchedAt', function () {
- this.clock.tick(5000);
- const user = this.User.build({ username: 'a user' });
- this.clock.restore();
- expect(Number(user.touchedAt)).to.equal(5000);
- });
- });
- describe('allowNull date', () => {
- 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', async function () {
- const date = new Date('2018-01-01T12:12:12.000Z');
- await this.User.build({ username: 'a user', dateAllowNullTrue: date }).save();
- const user = await this.User.findOne({ where: { username: 'a user' } });
- expect(user.dateAllowNullTrue.toISOString()).to.equal(date.toISOString());
- });
- });
- describe('super user boolean', () => {
- it('should default to false', async function () {
- await this.User.build({
- username: 'a user',
- }).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', async function () {
- await this.User.build({
- username: 'a user',
- isSuperUser: 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', async function () {
- let callCount = 0;
- try {
- await this.User.build({
- username: 'a user',
- isSuperUser: 'INCORRECT_VALUE_TYPE',
- }).save();
- callCount += 1;
- } catch (error) {
- expect(callCount).to.equal(0);
- expect(error).to.exist;
- expect(error.message).to.exist;
- }
- });
- });
- });
- describe('complete', () => {
- it('gets triggered if an error occurs', async function () {
- try {
- await this.User.findOne({ where: ['asdasdasd'] });
- } catch (error) {
- expect(error).to.exist;
- expect(error.message).to.exist;
- }
- });
- it('gets triggered if everything was ok', async function () {
- const result = await this.User.count();
- expect(result).to.exist;
- });
- });
- describe('findAll', () => {
- beforeEach(async function () {
- this.ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: { type: DataTypes.STRING },
- },
- { paranoid: true },
- );
- this.ParanoidUser.hasOne(this.ParanoidUser, {
- as: 'paranoidParent',
- inverse: { as: 'paranoidChild' },
- });
- await this.ParanoidUser.sync({ force: true });
- });
- 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', async function () {
- await this.ParanoidUser.create({ username: 'cuss' });
- const users0 = await 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',
- }),
- });
- expect(users).to.have.length(0);
- });
- 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',
- }),
- });
- expect(users0).to.have.length(1);
- await users0[0].destroy();
- const users = await this.ParanoidUser.findAll({
- where: this.sequelize.or({
- username: 'cuss',
- }),
- });
- expect(users).to.have.length(0);
- });
- it('escapes a single 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('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('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('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,
- primaryKey: true,
- },
- email: DataTypes.STRING,
- });
- 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', 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;
- 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', 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', 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].setParanoidParent(linkedUser);
- expect(user.deletedAt).not.to.exist;
- });
- 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', 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 },
- bool_value2: { type: DataTypes.BOOLEAN, allowNull: true },
- bool_value3: { type: DataTypes.BOOLEAN, allowNull: true },
- },
- { timestamps: false, logging: false },
- );
- 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', 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', async function () {
- this.UserAssociationEqual = this.sequelize.define(
- 'UserAssociationEquals',
- {
- username: DataTypes.STRING,
- age: DataTypes.INTEGER,
- },
- { timestamps: false },
- );
- this.ProjectAssociationEqual = this.sequelize.define(
- 'ProjectAssocationEquals',
- {
- title: DataTypes.STRING,
- overdue_days: DataTypes.INTEGER,
- },
- { timestamps: false },
- );
- this.UserAssociationEqual.hasMany(this.ProjectAssociationEqual, {
- as: 'Projects',
- foreignKey: 'userId',
- });
- this.ProjectAssociationEqual.belongsTo(this.UserAssociationEqual, {
- as: 'Users',
- foreignKey: 'userId',
- });
- 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', async function () {
- const User = this.sequelize.define(
- 'UserHelper',
- {
- username: DataTypes.STRING,
- },
- { timestamps: false, logging: false },
- );
- await User.sync();
- const user = User.build({ username: 'foo' });
- expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null });
- });
- });
- describe('isSoftDeleted', () => {
- beforeEach(async function () {
- this.ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: { type: DataTypes.STRING },
- },
- { paranoid: true },
- );
- await this.ParanoidUser.sync({ force: true });
- });
- 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', 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', 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;
- const user = await users[0].reload({ paranoid: false });
- expect(user.isSoftDeleted()).to.be.true;
- });
- it('works with custom `deletedAt` field name', async function () {
- this.ParanoidUserWithCustomDeletedAt = this.sequelize.define(
- 'ParanoidUserWithCustomDeletedAt',
- {
- username: { type: DataTypes.STRING },
- },
- {
- deletedAt: 'deletedAtThisTime',
- paranoid: true,
- },
- );
- this.ParanoidUserWithCustomDeletedAt.hasOne(this.ParanoidUser);
- 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;
- await users[0].destroy();
- expect(users[0].isSoftDeleted()).to.be.true;
- const user = await users[0].reload({ paranoid: false });
- expect(user.isSoftDeleted()).to.be.true;
- });
- });
- describe('restore', () => {
- it('returns an error if the model is not paranoid', async function () {
- const user = await this.User.create({ username: 'Peter', secretValue: '42' });
- await expect(user.restore()).to.be.rejectedWith(Error, 'Model is not paranoid');
- });
- it('restores a previously deleted model', async function () {
- const ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: DataTypes.STRING,
- secretValue: DataTypes.STRING,
- data: DataTypes.STRING,
- intVal: { type: DataTypes.INTEGER, defaultValue: 1 },
- },
- {
- paranoid: true,
- },
- );
- const data = [
- { username: 'Peter', secretValue: '42' },
- { username: 'Paul', secretValue: '43' },
- { username: 'Bob', secretValue: '44' },
- ];
- 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', async function () {
- const ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: DataTypes.STRING,
- destroyTime: DataTypes.DATE,
- },
- { paranoid: true, deletedAt: 'destroyTime' },
- );
- 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', async function () {
- const ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: DataTypes.STRING,
- deletedAt: { type: DataTypes.DATE, field: 'deleted_at' },
- },
- { paranoid: true },
- );
- 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', async function () {
- const ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: DataTypes.STRING,
- destroyTime: { type: DataTypes.DATE, field: 'destroy_time' },
- },
- { paranoid: true, deletedAt: 'destroyTime' },
- );
- 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', async function () {
- const ParanoidUser = this.sequelize.define(
- 'ParanoidUser',
- {
- username: DataTypes.STRING,
- deletedAt: { type: DataTypes.DATE, defaultValue: new Date(0) },
- },
- { paranoid: true },
- );
- 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());
- });
- });
- });
|