instance.test.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. 'use strict';
  2. const chai = require('chai');
  3. const expect = chai.expect;
  4. const Support = require('./support');
  5. const { DataTypes, sql } = require('@sequelize/core');
  6. const dialect = Support.getTestDialect();
  7. const sinon = require('sinon');
  8. const isUUID = require('validator').isUUID;
  9. describe(Support.getTestDialectTeaser('Instance'), () => {
  10. before(function () {
  11. this.clock = sinon.useFakeTimers();
  12. });
  13. afterEach(function () {
  14. this.clock.reset();
  15. });
  16. after(function () {
  17. this.clock.restore();
  18. });
  19. beforeEach(async function () {
  20. this.User = this.sequelize.define('User', {
  21. username: { type: DataTypes.STRING },
  22. uuidv1: { type: DataTypes.UUID, defaultValue: sql.uuidV1 },
  23. uuidv4: { type: DataTypes.UUID, defaultValue: sql.uuidV4 },
  24. touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
  25. aNumber: { type: DataTypes.INTEGER },
  26. bNumber: { type: DataTypes.INTEGER },
  27. aDate: { type: DataTypes.DATE },
  28. validateTest: {
  29. type: DataTypes.INTEGER,
  30. allowNull: true,
  31. validate: { isInt: true },
  32. },
  33. validateCustom: {
  34. type: DataTypes.STRING,
  35. allowNull: true,
  36. validate: { len: { msg: 'Length failed.', args: [1, 20] } },
  37. },
  38. dateAllowNullTrue: {
  39. type: DataTypes.DATE,
  40. allowNull: true,
  41. },
  42. isSuperUser: {
  43. type: DataTypes.BOOLEAN,
  44. defaultValue: false,
  45. },
  46. });
  47. await this.User.sync({ force: true });
  48. });
  49. describe('Escaping', () => {
  50. it('is done properly for special characters', async function () {
  51. // Ideally we should test more: "\0\n\r\b\t\\\'\"\x1a"
  52. // But this causes sqlite to fail and exits the entire test suite immediately
  53. const bio = `${dialect}'"\n`; // Need to add the dialect here so in case of failure I know what DB it failed for
  54. const u1 = await this.User.create({ username: bio });
  55. const u2 = await this.User.findByPk(u1.id);
  56. expect(u2.username).to.equal(bio);
  57. });
  58. });
  59. describe('isNewRecord', () => {
  60. it('returns true for non-saved objects', function () {
  61. const user = this.User.build({ username: 'user' });
  62. expect(user.id).to.be.null;
  63. expect(user.isNewRecord).to.be.ok;
  64. });
  65. it('returns false for saved objects', async function () {
  66. const user = await this.User.build({ username: 'user' }).save();
  67. expect(user.isNewRecord).to.not.be.ok;
  68. });
  69. it('returns false for created objects', async function () {
  70. const user = await this.User.create({ username: 'user' });
  71. expect(user.isNewRecord).to.not.be.ok;
  72. });
  73. it('returns false for upserted objects', async function () {
  74. if (!Support.sequelize.dialect.supports.upserts) {
  75. return;
  76. }
  77. // adding id here so MSSQL doesn't fail. It needs a primary key to upsert
  78. const [user] = await this.User.upsert({ id: 2, username: 'user' });
  79. expect(user.isNewRecord).to.not.be.ok;
  80. });
  81. it('returns false for objects found by find method', async function () {
  82. await this.User.create({ username: 'user' });
  83. const user = await this.User.create({ username: 'user' });
  84. const user0 = await this.User.findByPk(user.id);
  85. expect(user0.isNewRecord).to.not.be.ok;
  86. });
  87. it('returns false for objects found by findAll method', async function () {
  88. const users = [];
  89. for (let i = 0; i < 10; i++) {
  90. users[i] = { username: 'user' };
  91. }
  92. await this.User.bulkCreate(users);
  93. const users0 = await this.User.findAll();
  94. for (const u of users0) {
  95. expect(u.isNewRecord).to.not.be.ok;
  96. }
  97. });
  98. });
  99. describe('default values', () => {
  100. describe('uuid', () => {
  101. it('should store a string in uuidv1 and uuidv4', function () {
  102. const user = this.User.build({ username: 'a user' });
  103. expect(user.uuidv1).to.be.a('string');
  104. expect(user.uuidv4).to.be.a('string');
  105. });
  106. it('should store a string of length 36 in uuidv1 and uuidv4', function () {
  107. const user = this.User.build({ username: 'a user' });
  108. expect(user.uuidv1).to.have.length(36);
  109. expect(user.uuidv4).to.have.length(36);
  110. });
  111. it('should store a valid uuid in uuidv1 and uuidv4 that conforms to the UUID v1 and v4 specifications', function () {
  112. const user = this.User.build({ username: 'a user' });
  113. expect(isUUID(user.uuidv1)).to.be.true;
  114. expect(isUUID(user.uuidv4, 4)).to.be.true;
  115. });
  116. it('should store a valid uuid if the multiple primary key fields used', function () {
  117. const Person = this.sequelize.define('Person', {
  118. id1: {
  119. type: DataTypes.UUID,
  120. defaultValue: sql.uuidV1,
  121. primaryKey: true,
  122. },
  123. id2: {
  124. type: DataTypes.UUID,
  125. defaultValue: sql.uuidV4,
  126. primaryKey: true,
  127. },
  128. });
  129. const person = Person.build({});
  130. expect(person.id1).to.be.ok;
  131. expect(person.id1).to.have.length(36);
  132. expect(person.id2).to.be.ok;
  133. expect(person.id2).to.have.length(36);
  134. });
  135. });
  136. describe('current date', () => {
  137. it('should store a date in touchedAt', function () {
  138. const user = this.User.build({ username: 'a user' });
  139. expect(user.touchedAt).to.be.instanceof(Date);
  140. });
  141. it('should store the current date in touchedAt', function () {
  142. this.clock.tick(5000);
  143. const user = this.User.build({ username: 'a user' });
  144. this.clock.restore();
  145. expect(Number(user.touchedAt)).to.equal(5000);
  146. });
  147. });
  148. describe('allowNull date', () => {
  149. it('should be just "null" and not Date with Invalid Date', async function () {
  150. await this.User.build({ username: 'a user' }).save();
  151. const user = await this.User.findOne({ where: { username: 'a user' } });
  152. expect(user.dateAllowNullTrue).to.be.null;
  153. });
  154. it('should be the same valid date when saving the date', async function () {
  155. const date = new Date('2018-01-01T12:12:12.000Z');
  156. await this.User.build({ username: 'a user', dateAllowNullTrue: date }).save();
  157. const user = await this.User.findOne({ where: { username: 'a user' } });
  158. expect(user.dateAllowNullTrue.toISOString()).to.equal(date.toISOString());
  159. });
  160. });
  161. describe('super user boolean', () => {
  162. it('should default to false', async function () {
  163. await this.User.build({
  164. username: 'a user',
  165. }).save();
  166. const user = await this.User.findOne({
  167. where: {
  168. username: 'a user',
  169. },
  170. });
  171. expect(user.isSuperUser).to.be.false;
  172. });
  173. it('should override default when given truthy boolean', async function () {
  174. await this.User.build({
  175. username: 'a user',
  176. isSuperUser: true,
  177. }).save();
  178. const user = await this.User.findOne({
  179. where: {
  180. username: 'a user',
  181. },
  182. });
  183. expect(user.isSuperUser).to.be.true;
  184. });
  185. it('should throw error when given value of incorrect type', async function () {
  186. let callCount = 0;
  187. try {
  188. await this.User.build({
  189. username: 'a user',
  190. isSuperUser: 'INCORRECT_VALUE_TYPE',
  191. }).save();
  192. callCount += 1;
  193. } catch (error) {
  194. expect(callCount).to.equal(0);
  195. expect(error).to.exist;
  196. expect(error.message).to.exist;
  197. }
  198. });
  199. });
  200. });
  201. describe('complete', () => {
  202. it('gets triggered if an error occurs', async function () {
  203. try {
  204. await this.User.findOne({ where: ['asdasdasd'] });
  205. } catch (error) {
  206. expect(error).to.exist;
  207. expect(error.message).to.exist;
  208. }
  209. });
  210. it('gets triggered if everything was ok', async function () {
  211. const result = await this.User.count();
  212. expect(result).to.exist;
  213. });
  214. });
  215. describe('findAll', () => {
  216. beforeEach(async function () {
  217. this.ParanoidUser = this.sequelize.define(
  218. 'ParanoidUser',
  219. {
  220. username: { type: DataTypes.STRING },
  221. },
  222. { paranoid: true },
  223. );
  224. this.ParanoidUser.hasOne(this.ParanoidUser, {
  225. as: 'paranoidParent',
  226. inverse: { as: 'paranoidChild' },
  227. });
  228. await this.ParanoidUser.sync({ force: true });
  229. });
  230. it('sql should have paranoid condition', async function () {
  231. await this.ParanoidUser.create({ username: 'cuss' });
  232. const users0 = await this.ParanoidUser.findAll();
  233. expect(users0).to.have.length(1);
  234. await users0[0].destroy();
  235. const users = await this.ParanoidUser.findAll();
  236. expect(users).to.have.length(0);
  237. });
  238. it('sequelize.and as where should include paranoid condition', async function () {
  239. await this.ParanoidUser.create({ username: 'cuss' });
  240. const users0 = await this.ParanoidUser.findAll({
  241. where: this.sequelize.and({
  242. username: 'cuss',
  243. }),
  244. });
  245. expect(users0).to.have.length(1);
  246. await users0[0].destroy();
  247. const users = await this.ParanoidUser.findAll({
  248. where: this.sequelize.and({
  249. username: 'cuss',
  250. }),
  251. });
  252. expect(users).to.have.length(0);
  253. });
  254. it('sequelize.or as where should include paranoid condition', async function () {
  255. await this.ParanoidUser.create({ username: 'cuss' });
  256. const users0 = await this.ParanoidUser.findAll({
  257. where: this.sequelize.or({
  258. username: 'cuss',
  259. }),
  260. });
  261. expect(users0).to.have.length(1);
  262. await users0[0].destroy();
  263. const users = await this.ParanoidUser.findAll({
  264. where: this.sequelize.or({
  265. username: 'cuss',
  266. }),
  267. });
  268. expect(users).to.have.length(0);
  269. });
  270. it('escapes a single single quotes properly in where clauses', async function () {
  271. await this.User.create({ username: "user'name" });
  272. const users = await this.User.findAll({
  273. where: { username: "user'name" },
  274. });
  275. expect(users.length).to.equal(1);
  276. expect(users[0].username).to.equal("user'name");
  277. });
  278. it('escapes two single quotes properly in where clauses', async function () {
  279. await this.User.create({ username: "user''name" });
  280. const users = await this.User.findAll({
  281. where: { username: "user''name" },
  282. });
  283. expect(users.length).to.equal(1);
  284. expect(users[0].username).to.equal("user''name");
  285. });
  286. it('returns the timestamps if no attributes have been specified', async function () {
  287. await this.User.create({ username: 'fnord' });
  288. const users = await this.User.findAll();
  289. expect(users[0].createdAt).to.exist;
  290. });
  291. it('does not return the timestamps if the username attribute has been specified', async function () {
  292. await this.User.create({ username: 'fnord' });
  293. const users = await this.User.findAll({ attributes: ['username'] });
  294. expect(users[0].createdAt).not.to.exist;
  295. expect(users[0].username).to.exist;
  296. });
  297. it('creates the deletedAt property, when defining paranoid as true', async function () {
  298. await this.ParanoidUser.create({ username: 'fnord' });
  299. const users = await this.ParanoidUser.findAll();
  300. expect(users[0].deletedAt).to.be.null;
  301. });
  302. it('destroys a record with a primary key of something other than id', async function () {
  303. const UserDestroy = this.sequelize.define('UserDestroy', {
  304. newId: {
  305. type: DataTypes.STRING,
  306. primaryKey: true,
  307. },
  308. email: DataTypes.STRING,
  309. });
  310. await UserDestroy.sync();
  311. await UserDestroy.create({ newId: '123ABC', email: 'hello' });
  312. const user = await UserDestroy.findOne({ where: { email: 'hello' } });
  313. await user.destroy();
  314. });
  315. it('sets deletedAt property to a specific date when deleting an instance', async function () {
  316. await this.ParanoidUser.create({ username: 'fnord' });
  317. const users = await this.ParanoidUser.findAll();
  318. await users[0].destroy();
  319. expect(users[0].deletedAt.getMonth).to.exist;
  320. const user = await users[0].reload({ paranoid: false });
  321. expect(user.deletedAt.getMonth).to.exist;
  322. });
  323. it('keeps the deletedAt-attribute with value null, when running update', async function () {
  324. await this.ParanoidUser.create({ username: 'fnord' });
  325. const users = await this.ParanoidUser.findAll();
  326. const user = await users[0].update({ username: 'newFnord' });
  327. expect(user.deletedAt).not.to.exist;
  328. });
  329. it('keeps the deletedAt-attribute with value null, when updating associations', async function () {
  330. await this.ParanoidUser.create({ username: 'fnord' });
  331. const users = await this.ParanoidUser.findAll();
  332. const linkedUser = await this.ParanoidUser.create({ username: 'linkedFnord' });
  333. const user = await users[0].setParanoidParent(linkedUser);
  334. expect(user.deletedAt).not.to.exist;
  335. });
  336. it('can reuse query option objects', async function () {
  337. await this.User.create({ username: 'fnord' });
  338. const query = { where: { username: 'fnord' } };
  339. const users = await this.User.findAll(query);
  340. expect(users[0].username).to.equal('fnord');
  341. const users0 = await this.User.findAll(query);
  342. expect(users0[0].username).to.equal('fnord');
  343. });
  344. });
  345. describe('findOne', () => {
  346. it('can reuse query option objects', async function () {
  347. await this.User.create({ username: 'fnord' });
  348. const query = { where: { username: 'fnord' } };
  349. const user = await this.User.findOne(query);
  350. expect(user.username).to.equal('fnord');
  351. const user0 = await this.User.findOne(query);
  352. expect(user0.username).to.equal('fnord');
  353. });
  354. it('returns null for null, undefined, and unset boolean values', async function () {
  355. const Setting = this.sequelize.define(
  356. 'SettingHelper',
  357. {
  358. setting_key: DataTypes.STRING,
  359. bool_value: { type: DataTypes.BOOLEAN, allowNull: true },
  360. bool_value2: { type: DataTypes.BOOLEAN, allowNull: true },
  361. bool_value3: { type: DataTypes.BOOLEAN, allowNull: true },
  362. },
  363. { timestamps: false, logging: false },
  364. );
  365. await Setting.sync({ force: true });
  366. await Setting.create({ setting_key: 'test', bool_value: null, bool_value2: undefined });
  367. const setting = await Setting.findOne({ where: { setting_key: 'test' } });
  368. expect(setting.bool_value).to.equal(null);
  369. expect(setting.bool_value2).to.equal(null);
  370. expect(setting.bool_value3).to.equal(null);
  371. });
  372. });
  373. describe('equals', () => {
  374. it('can compare records with Date field', async function () {
  375. const user1 = await this.User.create({ username: 'fnord' });
  376. const user2 = await this.User.findOne({ where: { username: 'fnord' } });
  377. expect(user1.equals(user2)).to.be.true;
  378. });
  379. it('does not compare the existence of associations', async function () {
  380. this.UserAssociationEqual = this.sequelize.define(
  381. 'UserAssociationEquals',
  382. {
  383. username: DataTypes.STRING,
  384. age: DataTypes.INTEGER,
  385. },
  386. { timestamps: false },
  387. );
  388. this.ProjectAssociationEqual = this.sequelize.define(
  389. 'ProjectAssocationEquals',
  390. {
  391. title: DataTypes.STRING,
  392. overdue_days: DataTypes.INTEGER,
  393. },
  394. { timestamps: false },
  395. );
  396. this.UserAssociationEqual.hasMany(this.ProjectAssociationEqual, {
  397. as: 'Projects',
  398. foreignKey: 'userId',
  399. });
  400. this.ProjectAssociationEqual.belongsTo(this.UserAssociationEqual, {
  401. as: 'Users',
  402. foreignKey: 'userId',
  403. });
  404. await this.UserAssociationEqual.sync({ force: true });
  405. await this.ProjectAssociationEqual.sync({ force: true });
  406. const user1 = await this.UserAssociationEqual.create({ username: 'jimhalpert' });
  407. const project1 = await this.ProjectAssociationEqual.create({ title: 'A Cool Project' });
  408. await user1.setProjects([project1]);
  409. const user2 = await this.UserAssociationEqual.findOne({
  410. where: { username: 'jimhalpert' },
  411. include: [{ model: this.ProjectAssociationEqual, as: 'Projects' }],
  412. });
  413. const user3 = await this.UserAssociationEqual.create({ username: 'pambeesly' });
  414. expect(user1.get('Projects')).to.not.exist;
  415. expect(user2.get('Projects')).to.exist;
  416. expect(user1.equals(user2)).to.be.true;
  417. expect(user2.equals(user1)).to.be.true;
  418. expect(user1.equals(user3)).to.not.be.true;
  419. expect(user3.equals(user1)).to.not.be.true;
  420. });
  421. });
  422. describe('values', () => {
  423. it('returns all values', async function () {
  424. const User = this.sequelize.define(
  425. 'UserHelper',
  426. {
  427. username: DataTypes.STRING,
  428. },
  429. { timestamps: false, logging: false },
  430. );
  431. await User.sync();
  432. const user = User.build({ username: 'foo' });
  433. expect(user.get({ plain: true })).to.deep.equal({ username: 'foo', id: null });
  434. });
  435. });
  436. describe('isSoftDeleted', () => {
  437. beforeEach(async function () {
  438. this.ParanoidUser = this.sequelize.define(
  439. 'ParanoidUser',
  440. {
  441. username: { type: DataTypes.STRING },
  442. },
  443. { paranoid: true },
  444. );
  445. await this.ParanoidUser.sync({ force: true });
  446. });
  447. it('should return false when model is just created', async function () {
  448. const user = await this.ParanoidUser.create({ username: 'foo' });
  449. expect(user.isSoftDeleted()).to.be.false;
  450. });
  451. it('returns false if user is not soft deleted', async function () {
  452. await this.ParanoidUser.create({ username: 'fnord' });
  453. const users = await this.ParanoidUser.findAll();
  454. expect(users[0].isSoftDeleted()).to.be.false;
  455. });
  456. it('returns true if user is soft deleted', async function () {
  457. await this.ParanoidUser.create({ username: 'fnord' });
  458. const users = await this.ParanoidUser.findAll();
  459. await users[0].destroy();
  460. expect(users[0].isSoftDeleted()).to.be.true;
  461. const user = await users[0].reload({ paranoid: false });
  462. expect(user.isSoftDeleted()).to.be.true;
  463. });
  464. it('works with custom `deletedAt` field name', async function () {
  465. this.ParanoidUserWithCustomDeletedAt = this.sequelize.define(
  466. 'ParanoidUserWithCustomDeletedAt',
  467. {
  468. username: { type: DataTypes.STRING },
  469. },
  470. {
  471. deletedAt: 'deletedAtThisTime',
  472. paranoid: true,
  473. },
  474. );
  475. this.ParanoidUserWithCustomDeletedAt.hasOne(this.ParanoidUser);
  476. await this.ParanoidUserWithCustomDeletedAt.sync({ force: true });
  477. await this.ParanoidUserWithCustomDeletedAt.create({ username: 'fnord' });
  478. const users = await this.ParanoidUserWithCustomDeletedAt.findAll();
  479. expect(users[0].isSoftDeleted()).to.be.false;
  480. await users[0].destroy();
  481. expect(users[0].isSoftDeleted()).to.be.true;
  482. const user = await users[0].reload({ paranoid: false });
  483. expect(user.isSoftDeleted()).to.be.true;
  484. });
  485. });
  486. describe('restore', () => {
  487. it('returns an error if the model is not paranoid', async function () {
  488. const user = await this.User.create({ username: 'Peter', secretValue: '42' });
  489. await expect(user.restore()).to.be.rejectedWith(Error, 'Model is not paranoid');
  490. });
  491. it('restores a previously deleted model', async function () {
  492. const ParanoidUser = this.sequelize.define(
  493. 'ParanoidUser',
  494. {
  495. username: DataTypes.STRING,
  496. secretValue: DataTypes.STRING,
  497. data: DataTypes.STRING,
  498. intVal: { type: DataTypes.INTEGER, defaultValue: 1 },
  499. },
  500. {
  501. paranoid: true,
  502. },
  503. );
  504. const data = [
  505. { username: 'Peter', secretValue: '42' },
  506. { username: 'Paul', secretValue: '43' },
  507. { username: 'Bob', secretValue: '44' },
  508. ];
  509. await ParanoidUser.sync({ force: true });
  510. await ParanoidUser.bulkCreate(data);
  511. const user0 = await ParanoidUser.findOne({ where: { secretValue: '42' } });
  512. await user0.destroy();
  513. await user0.restore();
  514. const user = await ParanoidUser.findOne({ where: { secretValue: '42' } });
  515. expect(user).to.be.ok;
  516. expect(user.username).to.equal('Peter');
  517. });
  518. it('supports custom deletedAt field', async function () {
  519. const ParanoidUser = this.sequelize.define(
  520. 'ParanoidUser',
  521. {
  522. username: DataTypes.STRING,
  523. destroyTime: DataTypes.DATE,
  524. },
  525. { paranoid: true, deletedAt: 'destroyTime' },
  526. );
  527. await ParanoidUser.sync({ force: true });
  528. const user2 = await ParanoidUser.create({
  529. username: 'username',
  530. });
  531. const user1 = await user2.destroy();
  532. expect(user1.destroyTime).to.be.ok;
  533. expect(user1.deletedAt).to.not.be.ok;
  534. const user0 = await user1.restore();
  535. expect(user0.destroyTime).to.not.be.ok;
  536. const user = await ParanoidUser.findOne({ where: { username: 'username' } });
  537. expect(user).to.be.ok;
  538. expect(user.destroyTime).to.not.be.ok;
  539. expect(user.deletedAt).to.not.be.ok;
  540. });
  541. it('supports custom deletedAt field name', async function () {
  542. const ParanoidUser = this.sequelize.define(
  543. 'ParanoidUser',
  544. {
  545. username: DataTypes.STRING,
  546. deletedAt: { type: DataTypes.DATE, field: 'deleted_at' },
  547. },
  548. { paranoid: true },
  549. );
  550. await ParanoidUser.sync({ force: true });
  551. const user2 = await ParanoidUser.create({
  552. username: 'username',
  553. });
  554. const user1 = await user2.destroy();
  555. expect(user1.dataValues.deletedAt).to.be.ok;
  556. expect(user1.dataValues.deleted_at).to.not.be.ok;
  557. const user0 = await user1.restore();
  558. expect(user0.dataValues.deletedAt).to.not.be.ok;
  559. expect(user0.dataValues.deleted_at).to.not.be.ok;
  560. const user = await ParanoidUser.findOne({ where: { username: 'username' } });
  561. expect(user).to.be.ok;
  562. expect(user.deletedAt).to.not.be.ok;
  563. expect(user.deleted_at).to.not.be.ok;
  564. });
  565. it('supports custom deletedAt field and database column', async function () {
  566. const ParanoidUser = this.sequelize.define(
  567. 'ParanoidUser',
  568. {
  569. username: DataTypes.STRING,
  570. destroyTime: { type: DataTypes.DATE, field: 'destroy_time' },
  571. },
  572. { paranoid: true, deletedAt: 'destroyTime' },
  573. );
  574. await ParanoidUser.sync({ force: true });
  575. const user2 = await ParanoidUser.create({
  576. username: 'username',
  577. });
  578. const user1 = await user2.destroy();
  579. expect(user1.dataValues.destroyTime).to.be.ok;
  580. expect(user1.dataValues.deletedAt).to.not.be.ok;
  581. expect(user1.dataValues.destroy_time).to.not.be.ok;
  582. const user0 = await user1.restore();
  583. expect(user0.dataValues.destroyTime).to.not.be.ok;
  584. expect(user0.dataValues.destroy_time).to.not.be.ok;
  585. const user = await ParanoidUser.findOne({ where: { username: 'username' } });
  586. expect(user).to.be.ok;
  587. expect(user.destroyTime).to.not.be.ok;
  588. expect(user.destroy_time).to.not.be.ok;
  589. });
  590. it('supports custom default value', async function () {
  591. const ParanoidUser = this.sequelize.define(
  592. 'ParanoidUser',
  593. {
  594. username: DataTypes.STRING,
  595. deletedAt: { type: DataTypes.DATE, defaultValue: new Date(0) },
  596. },
  597. { paranoid: true },
  598. );
  599. await ParanoidUser.sync({ force: true });
  600. const user2 = await ParanoidUser.create({
  601. username: 'username',
  602. });
  603. const user1 = await user2.destroy();
  604. const user0 = await user1.restore();
  605. expect(user0.dataValues.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
  606. const user = await ParanoidUser.findOne({ where: { username: 'username' } });
  607. expect(user).to.be.ok;
  608. expect(user.deletedAt.toISOString()).to.equal(new Date(0).toISOString());
  609. });
  610. });
  611. });