find.test.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. 'use strict';
  2. const chai = require('chai');
  3. const { DataTypes, Op } = require('@sequelize/core');
  4. const expect = chai.expect;
  5. const Support = require('../../support');
  6. describe(Support.getTestDialectTeaser('Model'), () => {
  7. describe('scopes', () => {
  8. beforeEach(async function () {
  9. this.ScopeMe = this.sequelize.define(
  10. 'ScopeMe',
  11. {
  12. username: DataTypes.STRING,
  13. email: DataTypes.STRING,
  14. access_level: DataTypes.INTEGER,
  15. other_value: DataTypes.INTEGER,
  16. parent_id: DataTypes.INTEGER,
  17. },
  18. {
  19. defaultScope: {
  20. where: {
  21. access_level: {
  22. [Op.gte]: 5,
  23. },
  24. },
  25. },
  26. scopes: {
  27. highValue: {
  28. where: {
  29. other_value: {
  30. [Op.gte]: 10,
  31. },
  32. },
  33. },
  34. andScope: {
  35. where: {
  36. [Op.and]: [
  37. {
  38. email: {
  39. [Op.like]: '%@sequelizejs.com',
  40. },
  41. },
  42. { access_level: 3 },
  43. ],
  44. },
  45. },
  46. },
  47. },
  48. );
  49. this.DefaultScopeExclude = this.sequelize.define(
  50. 'DefaultScopeExclude',
  51. {
  52. name: DataTypes.STRING,
  53. other_value: {
  54. type: DataTypes.STRING,
  55. field: 'otherValue',
  56. },
  57. },
  58. {
  59. defaultScope: {
  60. attributes: {
  61. exclude: ['name'],
  62. },
  63. },
  64. },
  65. );
  66. this.ScopeMe.hasMany(this.DefaultScopeExclude);
  67. await this.sequelize.sync({ force: true });
  68. const records = [
  69. {
  70. username: 'tony',
  71. email: 'tony@sequelizejs.com',
  72. access_level: 3,
  73. other_value: 7,
  74. parent_id: 1,
  75. },
  76. {
  77. username: 'tobi',
  78. email: 'tobi@fakeemail.com',
  79. access_level: 10,
  80. other_value: 11,
  81. parent_id: 2,
  82. },
  83. {
  84. username: 'dan',
  85. email: 'dan@sequelizejs.com',
  86. access_level: 5,
  87. other_value: 10,
  88. parent_id: 1,
  89. },
  90. {
  91. username: 'fred',
  92. email: 'fred@foobar.com',
  93. access_level: 3,
  94. other_value: 7,
  95. parent_id: 1,
  96. },
  97. ];
  98. await this.ScopeMe.bulkCreate(records);
  99. });
  100. it('should be able use where in scope', async function () {
  101. const users = await this.ScopeMe.withScope({ where: { parent_id: 2 } }).findAll();
  102. expect(users).to.have.length(1);
  103. expect(users[0].username).to.equal('tobi');
  104. });
  105. it('should be able to combine scope and findAll where clauses', async function () {
  106. const users = await this.ScopeMe.withScope({ where: { parent_id: 1 } }).findAll({
  107. where: { access_level: 3 },
  108. });
  109. expect(users).to.have.length(2);
  110. expect(['tony', 'fred'].includes(users[0].username)).to.be.true;
  111. expect(['tony', 'fred'].includes(users[1].username)).to.be.true;
  112. });
  113. it('should be able to combine multiple scopes', async function () {
  114. const users = await this.ScopeMe.withScope('defaultScope', 'highValue').findAll();
  115. expect(users).to.have.length(2);
  116. expect(['tobi', 'dan'].includes(users[0].username)).to.be.true;
  117. expect(['tobi', 'dan'].includes(users[1].username)).to.be.true;
  118. });
  119. it('should be able to use a defaultScope if declared', async function () {
  120. const users = await this.ScopeMe.findAll();
  121. expect(users).to.have.length(2);
  122. expect([10, 5].includes(users[0].access_level)).to.be.true;
  123. expect([10, 5].includes(users[1].access_level)).to.be.true;
  124. expect(['dan', 'tobi'].includes(users[0].username)).to.be.true;
  125. expect(['dan', 'tobi'].includes(users[1].username)).to.be.true;
  126. });
  127. it('should be able to handle $and in scopes', async function () {
  128. const users = await this.ScopeMe.withScope('andScope').findAll();
  129. expect(users).to.have.length(1);
  130. expect(users[0].username).to.equal('tony');
  131. });
  132. describe('should not overwrite', () => {
  133. it('default scope with values from previous finds', async function () {
  134. const users0 = await this.ScopeMe.findAll({ where: { other_value: 10 } });
  135. expect(users0).to.have.length(1);
  136. const users = await this.ScopeMe.findAll();
  137. // This should not have other_value: 10
  138. expect(users).to.have.length(2);
  139. });
  140. it('other scopes with values from previous finds', async function () {
  141. const users0 = await this.ScopeMe.withScope('highValue').findAll({
  142. where: { access_level: 10 },
  143. });
  144. expect(users0).to.have.length(1);
  145. const users = await this.ScopeMe.withScope('highValue').findAll();
  146. // This should not have other_value: 10
  147. expect(users).to.have.length(2);
  148. });
  149. });
  150. it('should have no problem performing findOrCreate', async function () {
  151. const [user] = await this.ScopeMe.findOrCreate({ where: { username: 'fake' } });
  152. expect(user.username).to.equal('fake');
  153. });
  154. it('should work when included with default scope', async function () {
  155. await this.ScopeMe.findOne({
  156. include: [this.DefaultScopeExclude],
  157. });
  158. });
  159. });
  160. describe('scope in associations', () => {
  161. it('should work when association with a virtual column queried with default scope', async function () {
  162. const Game = this.sequelize.define('Game', {
  163. name: DataTypes.TEXT,
  164. });
  165. const User = this.sequelize.define(
  166. 'User',
  167. {
  168. login: DataTypes.TEXT,
  169. session: {
  170. type: DataTypes.VIRTUAL,
  171. get() {
  172. return 'New';
  173. },
  174. },
  175. },
  176. {
  177. defaultScope: {
  178. attributes: {
  179. exclude: ['login'],
  180. },
  181. },
  182. },
  183. );
  184. Game.hasMany(User);
  185. await this.sequelize.sync({ force: true });
  186. const games = await Game.findAll({
  187. include: [
  188. {
  189. model: User,
  190. },
  191. ],
  192. });
  193. expect(games).to.have.lengthOf(0);
  194. });
  195. });
  196. });