associations.test.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  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 sinon = require('sinon');
  7. const dialect = Support.getTestDialect();
  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('associations', () => {
  34. describe('1:1', () => {
  35. describe('cascade onUpdate', () => {
  36. beforeEach(async function () {
  37. this.Projects = this.sequelize.define('Project', {
  38. title: DataTypes.STRING,
  39. });
  40. this.Tasks = this.sequelize.define('Task', {
  41. title: DataTypes.STRING,
  42. });
  43. this.Projects.hasOne(this.Tasks, { foreignKey: { onUpdate: 'cascade' }, hooks: true });
  44. await this.Projects.sync({ force: true });
  45. await this.Tasks.sync({ force: true });
  46. });
  47. it('on success', async function () {
  48. let beforeHook = false;
  49. let afterHook = false;
  50. this.Tasks.beforeUpdate(async () => {
  51. beforeHook = true;
  52. });
  53. this.Tasks.afterUpdate(async () => {
  54. afterHook = true;
  55. });
  56. const project = await this.Projects.create({ title: 'New Project' });
  57. const task = await this.Tasks.create({ title: 'New Task' });
  58. await project.setTask(task);
  59. await project.update({ id: 2 });
  60. expect(beforeHook).to.be.true;
  61. expect(afterHook).to.be.true;
  62. });
  63. it('on error', async function () {
  64. this.Tasks.afterUpdate(async () => {
  65. throw new Error('Whoops!');
  66. });
  67. const project = await this.Projects.create({ title: 'New Project' });
  68. const task = await this.Tasks.create({ title: 'New Task' });
  69. try {
  70. await project.setTask(task);
  71. } catch (error) {
  72. expect(error).to.be.instanceOf(Error);
  73. }
  74. });
  75. });
  76. // this test makes no sense: task was destroyed by the database through CASCADE.
  77. // there is no way for Sequelize to call its beforeDestroy/afterDestroy
  78. // describe('destroy() with cascade onDelete', () => {
  79. // beforeEach(async function () {
  80. // this.Projects = this.sequelize.define('Project', {
  81. // title: DataTypes.STRING,
  82. // });
  83. //
  84. // this.Tasks = this.sequelize.define('Task', {
  85. // title: DataTypes.STRING,
  86. // });
  87. //
  88. // this.Projects.hasOne(this.Tasks, { foreignKey: { onDelete: 'CASCADE' }, hooks: true });
  89. //
  90. // await this.sequelize.sync({ force: true });
  91. // });
  92. //
  93. // it('with no errors', async function () {
  94. // const beforeProject = sinon.spy();
  95. // const afterProject = sinon.spy();
  96. // const beforeTask = sinon.spy();
  97. // const afterTask = sinon.spy();
  98. //
  99. // this.Projects.beforeCreate(beforeProject);
  100. // this.Projects.afterCreate(afterProject);
  101. // this.Tasks.beforeDestroy(beforeTask);
  102. // this.Tasks.afterDestroy(afterTask);
  103. //
  104. // const project = await this.Projects.create({ title: 'New Project' });
  105. // const task = await this.Tasks.create({ title: 'New Task' });
  106. // await project.setTask(task);
  107. // await project.destroy();
  108. // expect(beforeProject).to.have.been.calledOnce;
  109. // expect(afterProject).to.have.been.calledOnce;
  110. // expect(beforeTask).to.have.been.calledOnce;
  111. // expect(afterTask).to.have.been.calledOnce;
  112. // });
  113. //
  114. // it('with errors', async function () {
  115. // const CustomErrorText = 'Whoops!';
  116. // let beforeProject = false;
  117. // let afterProject = false;
  118. // let beforeTask = false;
  119. // let afterTask = false;
  120. //
  121. // this.Projects.beforeCreate(async () => {
  122. // beforeProject = true;
  123. // });
  124. //
  125. // this.Projects.afterCreate(async () => {
  126. // afterProject = true;
  127. // });
  128. //
  129. // this.Tasks.beforeDestroy(async () => {
  130. // beforeTask = true;
  131. // throw new Error(CustomErrorText);
  132. // });
  133. //
  134. // this.Tasks.afterDestroy(async () => {
  135. // afterTask = true;
  136. // });
  137. //
  138. // const project = await this.Projects.create({ title: 'New Project' });
  139. // const task = await this.Tasks.create({ title: 'New Task' });
  140. // await project.setTask(task);
  141. // await expect(project.destroy()).to.eventually.be.rejectedWith(CustomErrorText);
  142. // expect(beforeProject).to.be.true;
  143. // expect(afterProject).to.be.true;
  144. // expect(beforeTask).to.be.true;
  145. // expect(afterTask).to.be.false;
  146. // });
  147. // });
  148. describe('no cascade update', () => {
  149. beforeEach(async function () {
  150. this.Projects = this.sequelize.define('Project', {
  151. title: DataTypes.STRING,
  152. });
  153. this.Tasks = this.sequelize.define('Task', {
  154. title: DataTypes.STRING,
  155. });
  156. this.Projects.hasOne(this.Tasks);
  157. this.Tasks.belongsTo(this.Projects);
  158. await this.Projects.sync({ force: true });
  159. await this.Tasks.sync({ force: true });
  160. });
  161. it('on success', async function () {
  162. const beforeHook = sinon.spy();
  163. const afterHook = sinon.spy();
  164. this.Tasks.beforeUpdate(beforeHook);
  165. this.Tasks.afterUpdate(afterHook);
  166. const project = await this.Projects.create({ title: 'New Project' });
  167. const task = await this.Tasks.create({ title: 'New Task' });
  168. await project.setTask(task);
  169. await project.update({ id: 2 });
  170. expect(beforeHook).to.have.been.calledOnce;
  171. expect(afterHook).to.have.been.calledOnce;
  172. });
  173. it('on error', async function () {
  174. this.Tasks.afterUpdate(() => {
  175. throw new Error('Whoops!');
  176. });
  177. const project = await this.Projects.create({ title: 'New Project' });
  178. const task = await this.Tasks.create({ title: 'New Task' });
  179. await expect(project.setTask(task)).to.be.rejected;
  180. });
  181. });
  182. describe('no cascade delete', () => {
  183. beforeEach(async function () {
  184. this.Projects = this.sequelize.define('Project', {
  185. title: DataTypes.STRING,
  186. });
  187. this.Tasks = this.sequelize.define('Task', {
  188. title: DataTypes.STRING,
  189. });
  190. this.Projects.hasMany(this.Tasks);
  191. this.Tasks.belongsTo(this.Projects);
  192. await this.Projects.sync({ force: true });
  193. await this.Tasks.sync({ force: true });
  194. });
  195. describe('#remove', () => {
  196. it('with no errors', async function () {
  197. const beforeProject = sinon.spy();
  198. const afterProject = sinon.spy();
  199. const beforeTask = sinon.spy();
  200. const afterTask = sinon.spy();
  201. this.Projects.beforeCreate(beforeProject);
  202. this.Projects.afterCreate(afterProject);
  203. this.Tasks.beforeUpdate(beforeTask);
  204. this.Tasks.afterUpdate(afterTask);
  205. const project = await this.Projects.create({ title: 'New Project' });
  206. const task = await this.Tasks.create({ title: 'New Task' });
  207. await project.addTask(task);
  208. await project.removeTask(task);
  209. expect(beforeProject).to.have.been.called;
  210. expect(afterProject).to.have.been.called;
  211. expect(beforeTask).not.to.have.been.called;
  212. expect(afterTask).not.to.have.been.called;
  213. });
  214. it('with errors', async function () {
  215. const beforeProject = sinon.spy();
  216. const afterProject = sinon.spy();
  217. const beforeTask = sinon.spy();
  218. const afterTask = sinon.spy();
  219. this.Projects.beforeCreate(beforeProject);
  220. this.Projects.afterCreate(afterProject);
  221. this.Tasks.beforeUpdate(() => {
  222. beforeTask();
  223. throw new Error('Whoops!');
  224. });
  225. this.Tasks.afterUpdate(afterTask);
  226. const project = await this.Projects.create({ title: 'New Project' });
  227. const task = await this.Tasks.create({ title: 'New Task' });
  228. try {
  229. await project.addTask(task);
  230. } catch (error) {
  231. expect(error).to.be.instanceOf(Error);
  232. expect(beforeProject).to.have.been.calledOnce;
  233. expect(afterProject).to.have.been.calledOnce;
  234. expect(beforeTask).to.have.been.calledOnce;
  235. expect(afterTask).not.to.have.been.called;
  236. }
  237. });
  238. });
  239. });
  240. });
  241. describe('1:M', () => {
  242. // this test makes no sense: task was destroyed by the database through CASCADE.
  243. // there is no way for Sequelize to call its beforeDestroy/afterDestroy
  244. // describe('destroy with CASCADE', () => {
  245. // beforeEach(async function () {
  246. // this.Projects = this.sequelize.define('Project', {
  247. // title: DataTypes.STRING,
  248. // });
  249. //
  250. // this.Tasks = this.sequelize.define('Task', {
  251. // title: DataTypes.STRING,
  252. // });
  253. //
  254. // this.Projects.hasMany(this.Tasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  255. //
  256. // await this.Projects.sync({ force: true });
  257. //
  258. // await this.Tasks.sync({ force: true });
  259. // });
  260. //
  261. // it('with no errors', async function () {
  262. // const beforeProject = sinon.spy();
  263. // const afterProject = sinon.spy();
  264. // const beforeTask = sinon.spy();
  265. // const afterTask = sinon.spy();
  266. //
  267. // this.Projects.beforeCreate(beforeProject);
  268. // this.Projects.afterCreate(afterProject);
  269. // this.Tasks.beforeDestroy(beforeTask);
  270. // this.Tasks.afterDestroy(afterTask);
  271. //
  272. // const project = await this.Projects.create({ title: 'New Project' });
  273. // const task = await this.Tasks.create({ title: 'New Task' });
  274. // await project.addTask(task);
  275. // await project.destroy();
  276. // expect(beforeProject).to.have.been.calledOnce;
  277. // expect(afterProject).to.have.been.calledOnce;
  278. // expect(beforeTask).to.have.been.calledOnce;
  279. // expect(afterTask).to.have.been.calledOnce;
  280. // });
  281. //
  282. // it('with errors', async function () {
  283. // let beforeProject = false;
  284. // let afterProject = false;
  285. // let beforeTask = false;
  286. // let afterTask = false;
  287. //
  288. // this.Projects.beforeCreate(async () => {
  289. // beforeProject = true;
  290. // });
  291. //
  292. // this.Projects.afterCreate(async () => {
  293. // afterProject = true;
  294. // });
  295. //
  296. // this.Tasks.beforeDestroy(async () => {
  297. // beforeTask = true;
  298. // throw new Error('Whoops!');
  299. // });
  300. //
  301. // this.Tasks.afterDestroy(async () => {
  302. // afterTask = true;
  303. // });
  304. //
  305. // const project = await this.Projects.create({ title: 'New Project' });
  306. // const task = await this.Tasks.create({ title: 'New Task' });
  307. // await project.addTask(task);
  308. //
  309. // try {
  310. // await project.destroy();
  311. // } catch (error) {
  312. // expect(error).to.be.instanceOf(Error);
  313. // expect(beforeProject).to.be.true;
  314. // expect(afterProject).to.be.true;
  315. // expect(beforeTask).to.be.true;
  316. // expect(afterTask).to.be.false;
  317. // }
  318. // });
  319. // });
  320. describe('no cascade', () => {
  321. beforeEach(async function () {
  322. this.Projects = this.sequelize.define('Project', {
  323. title: DataTypes.STRING,
  324. });
  325. this.Tasks = this.sequelize.define('Task', {
  326. title: DataTypes.STRING,
  327. });
  328. this.Projects.hasMany(this.Tasks);
  329. this.Tasks.belongsTo(this.Projects);
  330. await this.sequelize.sync({ force: true });
  331. });
  332. describe('#remove', () => {
  333. it('with no errors', async function () {
  334. const beforeProject = sinon.spy();
  335. const afterProject = sinon.spy();
  336. const beforeTask = sinon.spy();
  337. const afterTask = sinon.spy();
  338. this.Projects.beforeCreate(beforeProject);
  339. this.Projects.afterCreate(afterProject);
  340. this.Tasks.beforeUpdate(beforeTask);
  341. this.Tasks.afterUpdate(afterTask);
  342. const project = await this.Projects.create({ title: 'New Project' });
  343. const task = await this.Tasks.create({ title: 'New Task' });
  344. await project.addTask(task);
  345. await project.removeTask(task);
  346. expect(beforeProject).to.have.been.called;
  347. expect(afterProject).to.have.been.called;
  348. expect(beforeTask).not.to.have.been.called;
  349. expect(afterTask).not.to.have.been.called;
  350. });
  351. it('with errors', async function () {
  352. let beforeProject = false;
  353. let afterProject = false;
  354. let beforeTask = false;
  355. let afterTask = false;
  356. this.Projects.beforeCreate(async () => {
  357. beforeProject = true;
  358. });
  359. this.Projects.afterCreate(async () => {
  360. afterProject = true;
  361. });
  362. this.Tasks.beforeUpdate(async () => {
  363. beforeTask = true;
  364. throw new Error('Whoops!');
  365. });
  366. this.Tasks.afterUpdate(async () => {
  367. afterTask = true;
  368. });
  369. const project = await this.Projects.create({ title: 'New Project' });
  370. const task = await this.Tasks.create({ title: 'New Task' });
  371. try {
  372. await project.addTask(task);
  373. } catch (error) {
  374. expect(error).to.be.instanceOf(Error);
  375. expect(beforeProject).to.be.true;
  376. expect(afterProject).to.be.true;
  377. expect(beforeTask).to.be.true;
  378. expect(afterTask).to.be.false;
  379. }
  380. });
  381. });
  382. });
  383. });
  384. describe('M:M', () => {
  385. describe('cascade', () => {
  386. beforeEach(async function () {
  387. this.Projects = this.sequelize.define('Project', {
  388. title: DataTypes.STRING,
  389. });
  390. this.Tasks = this.sequelize.define('Task', {
  391. title: DataTypes.STRING,
  392. });
  393. this.Projects.belongsToMany(this.Tasks, {
  394. foreignKey: { onDelete: 'CASCADE' },
  395. otherKey: { onDelete: 'CASCADE' },
  396. through: 'projects_and_tasks',
  397. hooks: true,
  398. });
  399. await this.sequelize.sync({ force: true });
  400. });
  401. describe('#remove', () => {
  402. it('with no errors', async function () {
  403. const beforeProject = sinon.spy();
  404. const afterProject = sinon.spy();
  405. const beforeTask = sinon.spy();
  406. const afterTask = sinon.spy();
  407. this.Projects.beforeCreate(beforeProject);
  408. this.Projects.afterCreate(afterProject);
  409. this.Tasks.beforeDestroy(beforeTask);
  410. this.Tasks.afterDestroy(afterTask);
  411. const project = await this.Projects.create({ title: 'New Project' });
  412. const task = await this.Tasks.create({ title: 'New Task' });
  413. await project.addTask(task);
  414. await project.destroy();
  415. expect(beforeProject).to.have.been.calledOnce;
  416. expect(afterProject).to.have.been.calledOnce;
  417. // Since Sequelize does not cascade M:M, these should be false
  418. expect(beforeTask).not.to.have.been.called;
  419. expect(afterTask).not.to.have.been.called;
  420. });
  421. it('with errors', async function () {
  422. let beforeProject = false;
  423. let afterProject = false;
  424. let beforeTask = false;
  425. let afterTask = false;
  426. this.Projects.beforeCreate(async () => {
  427. beforeProject = true;
  428. });
  429. this.Projects.afterCreate(async () => {
  430. afterProject = true;
  431. });
  432. this.Tasks.beforeDestroy(async () => {
  433. beforeTask = true;
  434. throw new Error('Whoops!');
  435. });
  436. this.Tasks.afterDestroy(async () => {
  437. afterTask = true;
  438. });
  439. const project = await this.Projects.create({ title: 'New Project' });
  440. const task = await this.Tasks.create({ title: 'New Task' });
  441. await project.addTask(task);
  442. await project.destroy();
  443. expect(beforeProject).to.be.true;
  444. expect(afterProject).to.be.true;
  445. expect(beforeTask).to.be.false;
  446. expect(afterTask).to.be.false;
  447. });
  448. });
  449. });
  450. describe('no cascade', () => {
  451. beforeEach(async function () {
  452. this.Projects = this.sequelize.define('Project', {
  453. title: DataTypes.STRING,
  454. });
  455. this.Tasks = this.sequelize.define('Task', {
  456. title: DataTypes.STRING,
  457. });
  458. this.Projects.belongsToMany(this.Tasks, { hooks: true, through: 'project_tasks' });
  459. this.Tasks.belongsToMany(this.Projects, { hooks: true, through: 'project_tasks' });
  460. await this.sequelize.sync({ force: true });
  461. });
  462. describe('#remove', () => {
  463. it('with no errors', async function () {
  464. const beforeProject = sinon.spy();
  465. const afterProject = sinon.spy();
  466. const beforeTask = sinon.spy();
  467. const afterTask = sinon.spy();
  468. this.Projects.beforeCreate(beforeProject);
  469. this.Projects.afterCreate(afterProject);
  470. this.Tasks.beforeUpdate(beforeTask);
  471. this.Tasks.afterUpdate(afterTask);
  472. const project = await this.Projects.create({ title: 'New Project' });
  473. const task = await this.Tasks.create({ title: 'New Task' });
  474. await project.addTask(task);
  475. await project.removeTask(task);
  476. expect(beforeProject).to.have.been.calledOnce;
  477. expect(afterProject).to.have.been.calledOnce;
  478. expect(beforeTask).not.to.have.been.called;
  479. expect(afterTask).not.to.have.been.called;
  480. });
  481. it('with errors', async function () {
  482. let beforeProject = false;
  483. let afterProject = false;
  484. let beforeTask = false;
  485. let afterTask = false;
  486. this.Projects.beforeCreate(async () => {
  487. beforeProject = true;
  488. });
  489. this.Projects.afterCreate(async () => {
  490. afterProject = true;
  491. });
  492. this.Tasks.beforeUpdate(async () => {
  493. beforeTask = true;
  494. throw new Error('Whoops!');
  495. });
  496. this.Tasks.afterUpdate(async () => {
  497. afterTask = true;
  498. });
  499. const project = await this.Projects.create({ title: 'New Project' });
  500. const task = await this.Tasks.create({ title: 'New Task' });
  501. await project.addTask(task);
  502. expect(beforeProject).to.be.true;
  503. expect(afterProject).to.be.true;
  504. expect(beforeTask).to.be.false;
  505. expect(afterTask).to.be.false;
  506. });
  507. });
  508. });
  509. });
  510. // NOTE: Reenable when FK constraints create table query is fixed when using hooks
  511. if (dialect !== 'mssql') {
  512. // this test makes no sense: task was destroyed by the database through CASCADE.
  513. // there is no way for Sequelize to call its beforeDestroy/afterDestroy
  514. // describe('multiple 1:M', () => {
  515. //
  516. // describe('destroy with onDelete CASCADE', () => {
  517. // beforeEach(async function () {
  518. // this.Projects = this.sequelize.define('Project', {
  519. // title: DataTypes.STRING,
  520. // });
  521. //
  522. // this.Tasks = this.sequelize.define('Task', {
  523. // title: DataTypes.STRING,
  524. // });
  525. //
  526. // this.MiniTasks = this.sequelize.define('MiniTask', {
  527. // mini_title: DataTypes.STRING,
  528. // });
  529. //
  530. // this.Projects.hasMany(this.Tasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  531. // this.Projects.hasMany(this.MiniTasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  532. // this.Tasks.hasMany(this.MiniTasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  533. //
  534. // await this.sequelize.sync({ force: true });
  535. // });
  536. //
  537. // it('with no errors', async function () {
  538. // let beforeProject = false;
  539. // let afterProject = false;
  540. // let beforeTask = false;
  541. // let afterTask = false;
  542. // let beforeMiniTask = false;
  543. // let afterMiniTask = false;
  544. //
  545. // this.Projects.beforeCreate(async () => {
  546. // beforeProject = true;
  547. // });
  548. //
  549. // this.Projects.afterCreate(async () => {
  550. // afterProject = true;
  551. // });
  552. //
  553. // this.Tasks.beforeDestroy(async () => {
  554. // beforeTask = true;
  555. // });
  556. //
  557. // this.Tasks.afterDestroy(async () => {
  558. // afterTask = true;
  559. // });
  560. //
  561. // this.MiniTasks.beforeDestroy(async () => {
  562. // beforeMiniTask = true;
  563. // });
  564. //
  565. // this.MiniTasks.afterDestroy(async () => {
  566. // afterMiniTask = true;
  567. // });
  568. //
  569. // const [project, minitask] = await Promise.all([
  570. // this.Projects.create({ title: 'New Project' }),
  571. // this.MiniTasks.create({ mini_title: 'New MiniTask' }),
  572. // ]);
  573. //
  574. // await project.addMiniTask(minitask);
  575. // await project.destroy();
  576. // expect(beforeProject).to.be.true;
  577. // expect(afterProject).to.be.true;
  578. // expect(beforeTask).to.be.false;
  579. // expect(afterTask).to.be.false;
  580. // expect(beforeMiniTask).to.be.true;
  581. // expect(afterMiniTask).to.be.true;
  582. // });
  583. //
  584. // it('with errors', async function () {
  585. // let beforeProject = false;
  586. // let afterProject = false;
  587. // let beforeTask = false;
  588. // let afterTask = false;
  589. // let beforeMiniTask = false;
  590. // let afterMiniTask = false;
  591. //
  592. // this.Projects.beforeCreate(async () => {
  593. // beforeProject = true;
  594. // });
  595. //
  596. // this.Projects.afterCreate(async () => {
  597. // afterProject = true;
  598. // });
  599. //
  600. // this.Tasks.beforeDestroy(async () => {
  601. // beforeTask = true;
  602. // });
  603. //
  604. // this.Tasks.afterDestroy(async () => {
  605. // afterTask = true;
  606. // });
  607. //
  608. // this.MiniTasks.beforeDestroy(async () => {
  609. // beforeMiniTask = true;
  610. // throw new Error('Whoops!');
  611. // });
  612. //
  613. // this.MiniTasks.afterDestroy(async () => {
  614. // afterMiniTask = true;
  615. // });
  616. //
  617. // try {
  618. // const [project, minitask] = await Promise.all([
  619. // this.Projects.create({ title: 'New Project' }),
  620. // this.MiniTasks.create({ mini_title: 'New MiniTask' }),
  621. // ]);
  622. //
  623. // await project.addMiniTask(minitask);
  624. // await project.destroy();
  625. // } catch {
  626. // expect(beforeProject).to.be.true;
  627. // expect(afterProject).to.be.true;
  628. // expect(beforeTask).to.be.false;
  629. // expect(afterTask).to.be.false;
  630. // expect(beforeMiniTask).to.be.true;
  631. // expect(afterMiniTask).to.be.false;
  632. // }
  633. // });
  634. // });
  635. // });
  636. // this test makes no sense: task was destroyed by the database through CASCADE.
  637. // there is no way for Sequelize to call its beforeDestroy/afterDestroy
  638. // describe('multiple 1:M sequential hooks', () => {
  639. // describe('destroy with onDelete CASCADE', () => {
  640. // beforeEach(async function () {
  641. // this.Projects = this.sequelize.define('Project', {
  642. // title: DataTypes.STRING,
  643. // });
  644. //
  645. // this.Tasks = this.sequelize.define('Task', {
  646. // title: DataTypes.STRING,
  647. // });
  648. //
  649. // this.MiniTasks = this.sequelize.define('MiniTask', {
  650. // mini_title: DataTypes.STRING,
  651. // });
  652. //
  653. // this.Projects.hasMany(this.Tasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  654. // this.Projects.hasMany(this.MiniTasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  655. // this.Tasks.hasMany(this.MiniTasks, { foreignKey: { onDelete: 'cascade' }, hooks: true });
  656. //
  657. // await this.sequelize.sync({ force: true });
  658. // });
  659. //
  660. // it('with no errors', async function () {
  661. // let beforeProject = false;
  662. // let afterProject = false;
  663. // let beforeTask = false;
  664. // let afterTask = false;
  665. // let beforeMiniTask = false;
  666. // let afterMiniTask = false;
  667. //
  668. // this.Projects.beforeCreate(async () => {
  669. // beforeProject = true;
  670. // });
  671. //
  672. // this.Projects.afterCreate(async () => {
  673. // afterProject = true;
  674. // });
  675. //
  676. // this.Tasks.beforeDestroy(async () => {
  677. // beforeTask = true;
  678. // });
  679. //
  680. // this.Tasks.afterDestroy(async () => {
  681. // afterTask = true;
  682. // });
  683. //
  684. // this.MiniTasks.beforeDestroy(async () => {
  685. // beforeMiniTask = true;
  686. // });
  687. //
  688. // this.MiniTasks.afterDestroy(async () => {
  689. // afterMiniTask = true;
  690. // });
  691. //
  692. // const [project0, task, minitask] = await Promise.all([
  693. // this.Projects.create({ title: 'New Project' }),
  694. // this.Tasks.create({ title: 'New Task' }),
  695. // this.MiniTasks.create({ mini_title: 'New MiniTask' }),
  696. // ]);
  697. //
  698. // await Promise.all([
  699. // task.addMiniTask(minitask),
  700. // project0.addTask(task),
  701. // ]);
  702. //
  703. // const project = project0;
  704. // await project.destroy();
  705. // expect(beforeProject).to.be.true;
  706. // expect(afterProject).to.be.true;
  707. // expect(beforeTask).to.be.true;
  708. // expect(afterTask).to.be.true;
  709. // expect(beforeMiniTask).to.be.true;
  710. // expect(afterMiniTask).to.be.true;
  711. // });
  712. //
  713. // it('with errors', async function () {
  714. // let beforeProject = false;
  715. // let afterProject = false;
  716. // let beforeTask = false;
  717. // let afterTask = false;
  718. // let beforeMiniTask = false;
  719. // let afterMiniTask = false;
  720. // const CustomErrorText = 'Whoops!';
  721. //
  722. // this.Projects.beforeCreate(() => {
  723. // beforeProject = true;
  724. // });
  725. //
  726. // this.Projects.afterCreate(() => {
  727. // afterProject = true;
  728. // });
  729. //
  730. // this.Tasks.beforeDestroy(() => {
  731. // beforeTask = true;
  732. // throw new Error(CustomErrorText);
  733. // });
  734. //
  735. // this.Tasks.afterDestroy(() => {
  736. // afterTask = true;
  737. // });
  738. //
  739. // this.MiniTasks.beforeDestroy(() => {
  740. // beforeMiniTask = true;
  741. // });
  742. //
  743. // this.MiniTasks.afterDestroy(() => {
  744. // afterMiniTask = true;
  745. // });
  746. //
  747. // const [project0, task, minitask] = await Promise.all([
  748. // this.Projects.create({ title: 'New Project' }),
  749. // this.Tasks.create({ title: 'New Task' }),
  750. // this.MiniTasks.create({ mini_title: 'New MiniTask' }),
  751. // ]);
  752. //
  753. // await Promise.all([
  754. // task.addMiniTask(minitask),
  755. // project0.addTask(task),
  756. // ]);
  757. //
  758. // const project = project0;
  759. // await expect(project.destroy()).to.eventually.be.rejectedWith(CustomErrorText);
  760. // expect(beforeProject).to.be.true;
  761. // expect(afterProject).to.be.true;
  762. // expect(beforeTask).to.be.true;
  763. // expect(afterTask).to.be.false;
  764. // expect(beforeMiniTask).to.be.false;
  765. // expect(afterMiniTask).to.be.false;
  766. // });
  767. // });
  768. // });
  769. }
  770. });
  771. });