hooks.test.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. 'use strict';
  2. const chai = require('chai');
  3. const expect = chai.expect;
  4. const Support = require('../support');
  5. const { DataTypes } = require('@sequelize/core');
  6. const dialect = Support.getTestDialect();
  7. const sinon = require('sinon');
  8. describe(Support.getTestDialectTeaser('Hooks'), () => {
  9. beforeEach(async function () {
  10. this.User = this.sequelize.define('User', {
  11. username: {
  12. type: DataTypes.STRING,
  13. allowNull: false,
  14. },
  15. mood: {
  16. type: DataTypes.ENUM(['happy', 'sad', 'neutral']),
  17. },
  18. });
  19. this.ParanoidUser = this.sequelize.define(
  20. 'ParanoidUser',
  21. {
  22. username: DataTypes.STRING,
  23. mood: {
  24. type: DataTypes.ENUM(['happy', 'sad', 'neutral']),
  25. },
  26. },
  27. {
  28. paranoid: true,
  29. },
  30. );
  31. await this.sequelize.sync({ force: true });
  32. });
  33. describe('passing DAO instances', () => {
  34. describe('beforeValidate / afterValidate', () => {
  35. it('should pass a DAO instance to the hook', async function () {
  36. let beforeHooked = false;
  37. let afterHooked = false;
  38. const User = this.sequelize.define(
  39. 'User',
  40. {
  41. username: DataTypes.STRING,
  42. },
  43. {
  44. hooks: {
  45. async beforeValidate(user) {
  46. expect(user).to.be.instanceof(User);
  47. beforeHooked = true;
  48. },
  49. async afterValidate(user) {
  50. expect(user).to.be.instanceof(User);
  51. afterHooked = true;
  52. },
  53. },
  54. },
  55. );
  56. await User.sync({ force: true });
  57. await User.create({ username: 'bob' });
  58. expect(beforeHooked).to.be.true;
  59. expect(afterHooked).to.be.true;
  60. });
  61. });
  62. describe('beforeCreate / afterCreate', () => {
  63. it('should pass a DAO instance to the hook', async function () {
  64. let beforeHooked = false;
  65. let afterHooked = false;
  66. const User = this.sequelize.define(
  67. 'User',
  68. {
  69. username: DataTypes.STRING,
  70. },
  71. {
  72. hooks: {
  73. async beforeCreate(user) {
  74. expect(user).to.be.instanceof(User);
  75. beforeHooked = true;
  76. },
  77. async afterCreate(user) {
  78. expect(user).to.be.instanceof(User);
  79. afterHooked = true;
  80. },
  81. },
  82. },
  83. );
  84. await User.sync({ force: true });
  85. await User.create({ username: 'bob' });
  86. expect(beforeHooked).to.be.true;
  87. expect(afterHooked).to.be.true;
  88. });
  89. });
  90. describe('beforeDestroy / afterDestroy', () => {
  91. it('should pass a DAO instance to the hook', async function () {
  92. let beforeHooked = false;
  93. let afterHooked = false;
  94. const User = this.sequelize.define(
  95. 'User',
  96. {
  97. username: DataTypes.STRING,
  98. },
  99. {
  100. hooks: {
  101. async beforeDestroy(user) {
  102. expect(user).to.be.instanceof(User);
  103. beforeHooked = true;
  104. },
  105. async afterDestroy(user) {
  106. expect(user).to.be.instanceof(User);
  107. afterHooked = true;
  108. },
  109. },
  110. },
  111. );
  112. await User.sync({ force: true });
  113. const user = await User.create({ username: 'bob' });
  114. await user.destroy();
  115. expect(beforeHooked).to.be.true;
  116. expect(afterHooked).to.be.true;
  117. });
  118. });
  119. describe('beforeUpdate / afterUpdate', () => {
  120. it('should pass a DAO instance to the hook', async function () {
  121. let beforeHooked = false;
  122. let afterHooked = false;
  123. const User = this.sequelize.define(
  124. 'User',
  125. {
  126. username: DataTypes.STRING,
  127. },
  128. {
  129. hooks: {
  130. async beforeUpdate(user) {
  131. expect(user).to.be.instanceof(User);
  132. beforeHooked = true;
  133. },
  134. async afterUpdate(user) {
  135. expect(user).to.be.instanceof(User);
  136. afterHooked = true;
  137. },
  138. },
  139. },
  140. );
  141. await User.sync({ force: true });
  142. const user = await User.create({ username: 'bob' });
  143. user.username = 'bawb';
  144. await user.save({ fields: ['username'] });
  145. expect(beforeHooked).to.be.true;
  146. expect(afterHooked).to.be.true;
  147. });
  148. });
  149. });
  150. describe('Model#sync', () => {
  151. describe('on success', () => {
  152. it('should run hooks', async function () {
  153. const beforeHook = sinon.spy();
  154. const afterHook = sinon.spy();
  155. this.User.beforeSync(beforeHook);
  156. this.User.afterSync(afterHook);
  157. await this.User.sync();
  158. expect(beforeHook).to.have.been.calledOnce;
  159. expect(afterHook).to.have.been.calledOnce;
  160. });
  161. it('should not run hooks when "hooks = false" option passed', async function () {
  162. const beforeHook = sinon.spy();
  163. const afterHook = sinon.spy();
  164. this.User.beforeSync(beforeHook);
  165. this.User.afterSync(afterHook);
  166. await this.User.sync({ hooks: false });
  167. expect(beforeHook).to.not.have.been.called;
  168. expect(afterHook).to.not.have.been.called;
  169. });
  170. });
  171. describe('on error', () => {
  172. it('should return an error from before', async function () {
  173. const beforeHook = sinon.spy();
  174. const afterHook = sinon.spy();
  175. this.User.beforeSync(() => {
  176. beforeHook();
  177. throw new Error('Whoops!');
  178. });
  179. this.User.afterSync(afterHook);
  180. await expect(this.User.sync()).to.be.rejected;
  181. expect(beforeHook).to.have.been.calledOnce;
  182. expect(afterHook).not.to.have.been.called;
  183. });
  184. it('should return an error from after', async function () {
  185. const beforeHook = sinon.spy();
  186. const afterHook = sinon.spy();
  187. this.User.beforeSync(beforeHook);
  188. this.User.afterSync(() => {
  189. afterHook();
  190. throw new Error('Whoops!');
  191. });
  192. await expect(this.User.sync()).to.be.rejected;
  193. expect(beforeHook).to.have.been.calledOnce;
  194. expect(afterHook).to.have.been.calledOnce;
  195. });
  196. });
  197. });
  198. describe('sequelize#sync', () => {
  199. describe('on success', () => {
  200. it('should run hooks', async function () {
  201. const beforeHook = sinon.spy();
  202. const afterHook = sinon.spy();
  203. const modelBeforeHook = sinon.spy();
  204. const modelAfterHook = sinon.spy();
  205. this.sequelize.beforeBulkSync(beforeHook);
  206. this.User.beforeSync(modelBeforeHook);
  207. this.User.afterSync(modelAfterHook);
  208. this.sequelize.afterBulkSync(afterHook);
  209. await this.sequelize.sync();
  210. expect(beforeHook).to.have.been.calledOnce;
  211. expect(modelBeforeHook).to.have.been.calledOnce;
  212. expect(modelAfterHook).to.have.been.calledOnce;
  213. expect(afterHook).to.have.been.calledOnce;
  214. });
  215. it('should not run hooks if "hooks = false" option passed', async function () {
  216. const beforeHook = sinon.spy();
  217. const afterHook = sinon.spy();
  218. const modelBeforeHook = sinon.spy();
  219. const modelAfterHook = sinon.spy();
  220. this.sequelize.beforeBulkSync(beforeHook);
  221. this.User.beforeSync(modelBeforeHook);
  222. this.User.afterSync(modelAfterHook);
  223. this.sequelize.afterBulkSync(afterHook);
  224. await this.sequelize.sync({ hooks: false });
  225. expect(beforeHook).to.not.have.been.called;
  226. expect(modelBeforeHook).to.not.have.been.called;
  227. expect(modelAfterHook).to.not.have.been.called;
  228. expect(afterHook).to.not.have.been.called;
  229. });
  230. afterEach(function () {
  231. this.sequelize.hooks.removeAllListeners();
  232. });
  233. });
  234. describe('on error', () => {
  235. it('should return an error from before', async () => {
  236. const beforeHook = sinon.spy();
  237. const afterHook = sinon.spy();
  238. const tmpSequelize = Support.createSingleTestSequelizeInstance();
  239. tmpSequelize.beforeBulkSync(() => {
  240. beforeHook();
  241. throw new Error('Whoops!');
  242. });
  243. tmpSequelize.afterBulkSync(afterHook);
  244. await expect(tmpSequelize.sync()).to.be.rejectedWith('Whoops!');
  245. expect(beforeHook).to.have.been.calledOnce;
  246. expect(afterHook).not.to.have.been.called;
  247. });
  248. it('should return an error from after', async () => {
  249. const beforeHook = sinon.spy();
  250. const afterHook = sinon.spy();
  251. const tmpSequelize = Support.createSingleTestSequelizeInstance();
  252. tmpSequelize.beforeBulkSync(beforeHook);
  253. tmpSequelize.afterBulkSync(() => {
  254. afterHook();
  255. throw new Error('Whoops!');
  256. });
  257. await expect(tmpSequelize.sync()).to.be.rejectedWith('Whoops!');
  258. expect(beforeHook).to.have.been.calledOnce;
  259. expect(afterHook).to.have.been.calledOnce;
  260. });
  261. afterEach(function () {
  262. this.sequelize.hooks.removeAllListeners();
  263. });
  264. });
  265. });
  266. describe('Sequelize hooks', () => {
  267. it('should run before/afterPoolAcquire hooks', async function () {
  268. if (dialect === 'sqlite3') {
  269. return this.skip();
  270. }
  271. const beforeHook = sinon.spy();
  272. const afterHook = sinon.spy();
  273. this.sequelize.addHook('beforePoolAcquire', beforeHook);
  274. this.sequelize.addHook('afterPoolAcquire', afterHook);
  275. await this.sequelize.authenticate();
  276. expect(beforeHook).to.have.been.calledOnce;
  277. expect(afterHook).to.have.been.calledOnce;
  278. });
  279. });
  280. });