increment.test.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import type { CreationOptional, InferAttributes, InferCreationAttributes } from '@sequelize/core';
  2. import { DataTypes, Model } from '@sequelize/core';
  3. import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
  4. import { expect } from 'chai';
  5. import { describe } from 'mocha';
  6. import sinon from 'sinon';
  7. import {
  8. beforeAll2,
  9. createSingleTransactionalTestSequelizeInstance,
  10. sequelize,
  11. setResetMode,
  12. } from '../support';
  13. describe('Model#increment', () => {
  14. setResetMode('destroy');
  15. context('with transactions', () => {
  16. if (!sequelize.dialect.supports.transactions) {
  17. return;
  18. }
  19. it('supports transactions', async () => {
  20. const transactionSequelize = await createSingleTransactionalTestSequelizeInstance(sequelize);
  21. class User extends Model<InferAttributes<User>> {
  22. @Attribute(DataTypes.INTEGER)
  23. @NotNull
  24. declare integer: number;
  25. }
  26. transactionSequelize.addModels([User]);
  27. await User.sync({ force: true });
  28. const user = await User.create({ integer: 3 });
  29. const t = await transactionSequelize.startUnmanagedTransaction();
  30. try {
  31. await user.increment('integer', { by: 2, transaction: t });
  32. const users1 = await User.findAll();
  33. const users2 = await User.findAll({ transaction: t });
  34. expect(users1[0].integer).to.equal(3);
  35. expect(users2[0].integer).to.equal(5);
  36. } finally {
  37. await t.rollback();
  38. }
  39. });
  40. });
  41. context('without transactions', () => {
  42. const vars = beforeAll2(async () => {
  43. const clock = sinon.useFakeTimers();
  44. class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
  45. declare id: number;
  46. declare updatedAt: CreationOptional<Date>;
  47. @Attribute(DataTypes.INTEGER)
  48. @NotNull
  49. declare integer1: number;
  50. @Attribute(DataTypes.INTEGER)
  51. @NotNull
  52. declare integer2: number;
  53. }
  54. sequelize.addModels([User]);
  55. await User.sync({ force: true });
  56. return { User, clock };
  57. });
  58. afterEach(() => {
  59. vars.clock.reset();
  60. });
  61. after(() => {
  62. vars.clock.restore();
  63. });
  64. beforeEach(async () => {
  65. await vars.User.create({ id: 1, integer1: 0, integer2: 0 });
  66. });
  67. if (sequelize.dialect.supports.returnValues === 'returning') {
  68. it('supports returning', async () => {
  69. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  70. await user1.increment('integer1', { by: 2 });
  71. expect(user1.integer1).to.equal(2);
  72. const user3 = await user1.increment('integer2', { by: 2, returning: false });
  73. expect(user3.integer2).to.equal(0);
  74. });
  75. }
  76. it('with array', async () => {
  77. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  78. await user1.increment(['integer1'], { by: 2 });
  79. const user3 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  80. expect(user3.integer1).to.equal(2);
  81. });
  82. it('with single field', async () => {
  83. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  84. await user1.increment('integer1', { by: 2 });
  85. const user3 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  86. expect(user3.integer1).to.equal(2);
  87. });
  88. it('with single field and no value', async () => {
  89. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  90. await user1.increment('integer1');
  91. const user2 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  92. expect(user2.integer1).to.equal(1);
  93. });
  94. it('should still work right with other concurrent updates', async () => {
  95. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  96. // Select the user again (simulating a concurrent query)
  97. const user2 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  98. await user2.update({
  99. integer1: user2.integer1 + 1,
  100. });
  101. await user1.increment(['integer1'], { by: 2 });
  102. const user5 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  103. expect(user5.integer1).to.equal(3);
  104. });
  105. it('should still work right with other concurrent increments', async () => {
  106. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  107. await Promise.all([
  108. user1.increment(['integer1'], { by: 2 }),
  109. user1.increment(['integer1'], { by: 2 }),
  110. user1.increment(['integer1'], { by: 2 }),
  111. ]);
  112. const user2 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  113. expect(user2.integer1).to.equal(6);
  114. });
  115. it('with key value pair', async () => {
  116. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  117. await user1.increment({ integer1: 1, integer2: 2 });
  118. const user3 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  119. expect(user3.integer1).to.equal(1);
  120. expect(user3.integer2).to.equal(2);
  121. });
  122. it('with negative value', async () => {
  123. const user1 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  124. await Promise.all([
  125. user1.increment('integer1', { by: -2 }),
  126. user1.increment(['integer1', 'integer2'], { by: -2 }),
  127. user1.increment({ integer1: -1, integer2: -2 }),
  128. ]);
  129. const user3 = await vars.User.findByPk(1, { rejectOnEmpty: true });
  130. expect(user3.integer1).to.equal(-5);
  131. expect(user3.integer2).to.equal(-4);
  132. });
  133. it('supports silent: true', async () => {
  134. const user = await vars.User.findByPk(1, { rejectOnEmpty: true });
  135. const oldDate = user.updatedAt;
  136. vars.clock.tick(1000);
  137. await user.increment('integer1', { by: 1, silent: true });
  138. const refreshedUser = await vars.User.findByPk(1, { rejectOnEmpty: true });
  139. expect(refreshedUser.updatedAt).to.equalTime(oldDate);
  140. });
  141. it('is disallowed if no primary key is present', async () => {
  142. const Foo = sequelize.define('Foo', {}, { noPrimaryKey: true });
  143. await Foo.sync({ force: true });
  144. const instance = await Foo.create({});
  145. await expect(instance.increment('id')).to.be.rejectedWith(
  146. 'but the model does not have a primary key attribute definition.',
  147. );
  148. });
  149. });
  150. });