destroy.test.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import type { CreationOptional, InferAttributes, InferCreationAttributes } from '@sequelize/core';
  2. import { DataTypes, Model, Op } from '@sequelize/core';
  3. import { Attribute, Table } from '@sequelize/core/decorators-legacy';
  4. import { expect } from 'chai';
  5. import {
  6. beforeAll2,
  7. createSingleTransactionalTestSequelizeInstance,
  8. sequelize,
  9. setResetMode,
  10. } from '../support';
  11. describe('destroy', () => {
  12. context('test-shared models', () => {
  13. setResetMode('truncate');
  14. const vars = beforeAll2(async () => {
  15. class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
  16. @Attribute(DataTypes.STRING)
  17. declare username: string | null;
  18. }
  19. @Table({ paranoid: true })
  20. class ParanoidUser extends Model<
  21. InferAttributes<ParanoidUser>,
  22. InferCreationAttributes<ParanoidUser>
  23. > {
  24. declare id: CreationOptional<number>;
  25. @Attribute(DataTypes.STRING)
  26. declare username: string | null;
  27. declare deletedAt: Date | null;
  28. }
  29. sequelize.addModels([User, ParanoidUser]);
  30. await sequelize.sync({ force: true });
  31. return { User, ParanoidUser };
  32. });
  33. it('throws an error if no where clause is given', async () => {
  34. const { User } = vars;
  35. await expect(User.destroy()).to.be.rejectedWith(
  36. Error,
  37. 'As a safeguard, the "destroy" static model method requires explicitly specifying a "where" option. If you actually mean to delete all rows in the table, set the option to a dummy condition such as sql`1 = 1`.',
  38. );
  39. });
  40. it('deletes all instances when given an empty where object', async () => {
  41. const { User } = vars;
  42. await User.bulkCreate([{ username: 'user1' }, { username: 'user2' }]);
  43. const affectedRows = await User.destroy({ where: {} });
  44. expect(affectedRows).to.equal(2);
  45. expect(await User.findAll()).to.have.lengthOf(0);
  46. });
  47. it('deletes values that match filter', async () => {
  48. const { User } = vars;
  49. const data = [{ username: 'Peter' }, { username: 'Paul' }, { username: 'Paul' }];
  50. await User.bulkCreate(data);
  51. await User.destroy({ where: { username: 'Paul' } });
  52. const users = await User.findAll();
  53. expect(users.length).to.equal(1);
  54. expect(users[0].username).to.equal('Peter');
  55. });
  56. it('returns the number of affected rows', async () => {
  57. const { User } = vars;
  58. const data = [{ username: 'Peter' }, { username: 'Paul' }, { username: 'Bob' }];
  59. await User.bulkCreate(data);
  60. const affectedRows = await User.destroy({ where: {} });
  61. expect(affectedRows).to.equal(3);
  62. });
  63. it('sets deletedAt to the current timestamp if paranoid is true', async () => {
  64. const { ParanoidUser } = vars;
  65. const data = [{ username: 'Peter' }, { username: 'Paul' }];
  66. await ParanoidUser.bulkCreate(data);
  67. // since we save in UTC, let's format to UTC time
  68. const date = new Date();
  69. await ParanoidUser.destroy({ where: { username: 'Paul' } });
  70. const users = await ParanoidUser.findAll();
  71. expect(users.length).to.equal(1);
  72. expect(users[0].username).to.equal('Peter');
  73. const deletedUsers = await ParanoidUser.findAll({
  74. paranoid: false,
  75. where: { deletedAt: { [Op.isNot]: null } },
  76. order: [['username', 'ASC']],
  77. });
  78. expect(deletedUsers[0].username).to.equal('Paul');
  79. expect(deletedUsers[0].deletedAt).to.be.closeToTime(date, 100);
  80. });
  81. it('does not set deletedAt for previously destroyed instances if paranoid is true', async () => {
  82. const { ParanoidUser } = vars;
  83. const [user1] = await ParanoidUser.bulkCreate([{ username: 'Toni' }, { username: 'Max' }]);
  84. const user = await ParanoidUser.findByPk(user1.id, { rejectOnEmpty: true });
  85. await user.destroy();
  86. await user.reload({ paranoid: false });
  87. const deletedAt = user.deletedAt!;
  88. await ParanoidUser.destroy({ where: {} });
  89. await user.reload({ paranoid: false });
  90. expect(user.deletedAt).to.equalTime(deletedAt);
  91. });
  92. it('permanently deletes a paranoid record if "force" is true', async () => {
  93. const { ParanoidUser } = vars;
  94. await ParanoidUser.create({ username: 'Bob' });
  95. await ParanoidUser.destroy({ where: {}, force: true });
  96. expect(await ParanoidUser.findAll({ paranoid: false })).to.be.empty;
  97. });
  98. it('should work if model is paranoid and has a where clause', async () => {
  99. const { ParanoidUser } = vars;
  100. await ParanoidUser.bulkCreate([{ username: 'foo' }, { username: 'bar' }]);
  101. await ParanoidUser.destroy({
  102. where: {
  103. username: 'bar',
  104. },
  105. });
  106. const users = await ParanoidUser.findAll();
  107. expect(users).to.have.length(1);
  108. expect(users[0].username).to.equal('foo');
  109. });
  110. });
  111. context('test-specific models', () => {
  112. if (sequelize.dialect.supports.transactions) {
  113. it('supports transactions', async () => {
  114. const transactionSequelize =
  115. await createSingleTransactionalTestSequelizeInstance(sequelize);
  116. const User = transactionSequelize.define('User', { username: DataTypes.STRING });
  117. await User.sync({ force: true });
  118. await User.create({ username: 'foo' });
  119. const transaction = await transactionSequelize.startUnmanagedTransaction();
  120. try {
  121. await User.destroy({
  122. where: {},
  123. transaction,
  124. });
  125. const count1 = await User.count();
  126. const count2 = await User.count({ transaction });
  127. expect(count1).to.equal(1);
  128. expect(count2).to.equal(0);
  129. } finally {
  130. await transaction.rollback();
  131. }
  132. });
  133. }
  134. it('works without a primary key', async () => {
  135. const Log = sequelize.define('Log', {
  136. client_id: DataTypes.INTEGER,
  137. content: DataTypes.TEXT,
  138. timestamp: DataTypes.DATE,
  139. });
  140. Log.removeAttribute('id');
  141. await Log.sync({ force: true });
  142. await Log.create({
  143. client_id: 13,
  144. content: 'Error!',
  145. timestamp: new Date(),
  146. });
  147. await Log.destroy({
  148. where: {
  149. client_id: 13,
  150. },
  151. });
  152. expect(await Log.findAll()).to.have.lengthOf(0);
  153. });
  154. it('maps the the column name', async () => {
  155. const UserProject = sequelize.define('UserProject', {
  156. userId: {
  157. type: DataTypes.INTEGER,
  158. columnName: 'user_id',
  159. },
  160. });
  161. await UserProject.sync({ force: true });
  162. await UserProject.create({ userId: 10 });
  163. await UserProject.destroy({ where: { userId: 10 } });
  164. expect(await UserProject.findAll()).to.have.lengthOf(0);
  165. });
  166. if (sequelize.dialect.supports.schemas) {
  167. it('supports table schema/prefix', async () => {
  168. @Table({ schema: 'prefix' })
  169. class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
  170. @Attribute(DataTypes.STRING)
  171. declare username: string | null;
  172. }
  173. sequelize.addModels([User]);
  174. await sequelize.queryInterface.createSchema('prefix');
  175. await User.sync({ force: true });
  176. const data = [{ username: 'Peter' }, { username: 'Peter' }, { username: 'Bob' }];
  177. await User.bulkCreate(data);
  178. await User.destroy({ where: { username: 'Peter' } });
  179. const users = await User.findAll({ order: ['id'] });
  180. expect(users.length).to.equal(1);
  181. expect(users[0].username).to.equal('Bob');
  182. });
  183. }
  184. });
  185. });