instance-validator.test.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. 'use strict';
  2. const chai = require('chai');
  3. const expect = chai.expect;
  4. const Support = require('../support');
  5. const {
  6. InstanceValidator,
  7. } = require('@sequelize/core/_non-semver-use-at-your-own-risk_/instance-validator.js');
  8. const sinon = require('sinon');
  9. const { DataTypes, ValidationError: SequelizeValidationError } = require('@sequelize/core');
  10. describe(Support.getTestDialectTeaser('InstanceValidator'), () => {
  11. beforeEach(function () {
  12. this.User = Support.sequelize.define('user', {
  13. fails: {
  14. type: DataTypes.BOOLEAN,
  15. validate: {
  16. isNotTrue(value) {
  17. if (value) {
  18. throw new Error('Manual model validation failure');
  19. }
  20. },
  21. },
  22. },
  23. });
  24. });
  25. it('configures itself to run hooks by default', () => {
  26. const instanceValidator = new InstanceValidator();
  27. expect(instanceValidator.options.hooks).to.equal(true);
  28. });
  29. describe('validate', () => {
  30. it('runs the validation sequence and hooks when the hooks option is true', function () {
  31. const instanceValidator = new InstanceValidator(this.User.build(), { hooks: true });
  32. const _validate = sinon.spy(instanceValidator, '_validate');
  33. const _validateAndRunHooks = sinon.spy(instanceValidator, '_validateAndRunHooks');
  34. instanceValidator.validate();
  35. expect(_validateAndRunHooks).to.have.been.calledOnce;
  36. expect(_validate).to.not.have.been.called;
  37. });
  38. it('runs the validation sequence but skips hooks if the hooks option is false', function () {
  39. const instanceValidator = new InstanceValidator(this.User.build(), { hooks: false });
  40. const _validate = sinon.spy(instanceValidator, '_validate');
  41. const _validateAndRunHooks = sinon.spy(instanceValidator, '_validateAndRunHooks');
  42. instanceValidator.validate();
  43. expect(_validate).to.have.been.calledOnce;
  44. expect(_validateAndRunHooks).to.not.have.been.called;
  45. });
  46. it('fulfills when validation is successful', async function () {
  47. const instanceValidator = new InstanceValidator(this.User.build());
  48. const result = instanceValidator.validate();
  49. await expect(result).to.be.fulfilled;
  50. });
  51. it('rejects with a validation error when validation fails', async function () {
  52. const instanceValidator = new InstanceValidator(this.User.build({ fails: true }));
  53. const result = instanceValidator.validate();
  54. await expect(result).to.be.rejectedWith(SequelizeValidationError);
  55. });
  56. it('has a useful default error message for not null validation failures', async () => {
  57. const User = Support.sequelize.define('user', {
  58. name: {
  59. type: DataTypes.STRING,
  60. allowNull: false,
  61. },
  62. });
  63. const instanceValidator = new InstanceValidator(User.build());
  64. const result = instanceValidator.validate();
  65. await expect(result).to.be.rejectedWith(
  66. SequelizeValidationError,
  67. /user\.name cannot be null/,
  68. );
  69. });
  70. });
  71. describe('_validateAndRunHooks', () => {
  72. beforeEach(function () {
  73. this.successfulInstanceValidator = new InstanceValidator(this.User.build());
  74. sinon.stub(this.successfulInstanceValidator, '_validate').resolves();
  75. });
  76. it('should run beforeValidate and afterValidate hooks when _validate is successful', async function () {
  77. const beforeValidate = sinon.spy();
  78. const afterValidate = sinon.spy();
  79. this.User.beforeValidate(beforeValidate);
  80. this.User.afterValidate(afterValidate);
  81. await expect(this.successfulInstanceValidator._validateAndRunHooks()).to.be.fulfilled;
  82. expect(beforeValidate).to.have.been.calledOnce;
  83. expect(afterValidate).to.have.been.calledOnce;
  84. });
  85. it('should run beforeValidate hook but not afterValidate hook when _validate is unsuccessful', async function () {
  86. const failingInstanceValidator = new InstanceValidator(this.User.build());
  87. sinon.stub(failingInstanceValidator, '_validate').rejects(new Error());
  88. const beforeValidate = sinon.spy();
  89. const afterValidate = sinon.spy();
  90. this.User.beforeValidate(beforeValidate);
  91. this.User.afterValidate(afterValidate);
  92. await expect(failingInstanceValidator._validateAndRunHooks()).to.be.rejected;
  93. expect(beforeValidate).to.have.been.calledOnce;
  94. expect(afterValidate).to.not.have.been.called;
  95. });
  96. it('should emit an error from after hook when afterValidate fails', async function () {
  97. this.User.afterValidate(() => {
  98. throw new Error('after validation error');
  99. });
  100. await expect(this.successfulInstanceValidator._validateAndRunHooks()).to.be.rejectedWith(
  101. 'after validation error',
  102. );
  103. });
  104. describe('validatedFailed hook', () => {
  105. it('should call validationFailed hook when validation fails', async function () {
  106. const failingInstanceValidator = new InstanceValidator(this.User.build());
  107. sinon.stub(failingInstanceValidator, '_validate').rejects(new Error());
  108. const validationFailedHook = sinon.spy();
  109. this.User.validationFailed(validationFailedHook);
  110. await expect(failingInstanceValidator._validateAndRunHooks()).to.be.rejected;
  111. expect(validationFailedHook).to.have.been.calledOnce;
  112. });
  113. it('should not replace the validation error in validationFailed hook by default', async function () {
  114. const failingInstanceValidator = new InstanceValidator(this.User.build());
  115. sinon.stub(failingInstanceValidator, '_validate').rejects(new SequelizeValidationError());
  116. const validationFailedHook = sinon.stub().resolves();
  117. this.User.validationFailed(validationFailedHook);
  118. const err = await expect(failingInstanceValidator._validateAndRunHooks()).to.be.rejected;
  119. expect(err.name).to.equal('SequelizeValidationError');
  120. });
  121. it('should replace the validation error if validationFailed hook creates a new error', async function () {
  122. const failingInstanceValidator = new InstanceValidator(this.User.build());
  123. sinon.stub(failingInstanceValidator, '_validate').rejects(new SequelizeValidationError());
  124. const validationFailedHook = sinon.stub().throws(new Error('validation failed hook error'));
  125. this.User.validationFailed(validationFailedHook);
  126. const err = await expect(failingInstanceValidator._validateAndRunHooks()).to.be.rejected;
  127. expect(err.message).to.equal('validation failed hook error');
  128. });
  129. });
  130. });
  131. });