schema.test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. 'use strict';
  2. const { expect } = require('chai');
  3. const { createSingleTestSequelizeInstance, sequelize: current } = require('../support');
  4. const { DataTypes, Op } = require('@sequelize/core');
  5. const SCHEMA_ONE = 'schema_one';
  6. const SCHEMA_TWO = 'schema_two';
  7. let locationId;
  8. describe('Model', () => {
  9. if (!current.dialect.supports.schemas) {
  10. return;
  11. }
  12. describe('global schema', () => {
  13. let schemaTwoSequelize;
  14. beforeEach('build restaurant tables', async function () {
  15. schemaTwoSequelize = createSingleTestSequelizeInstance({
  16. schema: SCHEMA_TWO,
  17. });
  18. this.RestaurantOne = schemaTwoSequelize.define(
  19. 'RestaurantOne',
  20. {
  21. foo: DataTypes.STRING,
  22. bar: DataTypes.STRING,
  23. },
  24. { schema: current.dialect.getDefaultSchema(), tableName: 'restaurants' },
  25. );
  26. this.LocationOne = schemaTwoSequelize.define(
  27. 'LocationOne',
  28. {
  29. name: DataTypes.STRING,
  30. },
  31. { schema: current.dialect.getDefaultSchema(), tableName: 'locations' },
  32. );
  33. this.RestaurantOne.belongsTo(this.LocationOne, {
  34. foreignKey: 'location_id',
  35. foreignKeyConstraints: false,
  36. as: 'location',
  37. });
  38. this.RestaurantTwo = schemaTwoSequelize.define(
  39. 'RestaurantTwo',
  40. {
  41. foo: DataTypes.STRING,
  42. bar: DataTypes.STRING,
  43. },
  44. { tableName: 'restaurants' },
  45. );
  46. this.LocationTwo = schemaTwoSequelize.define(
  47. 'LocationTwo',
  48. {
  49. name: DataTypes.STRING,
  50. },
  51. { tableName: 'locations' },
  52. );
  53. this.RestaurantTwo.belongsTo(this.LocationTwo, {
  54. foreignKey: 'location_id',
  55. foreignKeyConstraints: false,
  56. as: 'location',
  57. });
  58. await schemaTwoSequelize.createSchema(SCHEMA_TWO);
  59. await schemaTwoSequelize.sync({ force: true });
  60. });
  61. describe('Add data via model.create, retrieve via model.findOne', () => {
  62. it('should be able to sync model without schema option', function () {
  63. expect(this.RestaurantOne.table.schema).to.eq(current.dialect.getDefaultSchema());
  64. expect(this.RestaurantTwo.table.schema).to.equal(SCHEMA_TWO);
  65. });
  66. it('should be able to insert data into default table using create', async function () {
  67. await this.RestaurantOne.create({
  68. foo: 'one',
  69. });
  70. const obj0 = await this.RestaurantOne.findOne({
  71. where: { foo: 'one' },
  72. });
  73. expect(obj0).to.not.be.null;
  74. expect(obj0.foo).to.equal('one');
  75. const obj = await this.RestaurantTwo.findOne({
  76. where: { foo: 'one' },
  77. });
  78. expect(obj).to.be.null;
  79. });
  80. it('should be able to insert data into schema table using create', async function () {
  81. await this.RestaurantTwo.create({
  82. foo: 'two',
  83. });
  84. const obj0 = await this.RestaurantTwo.findOne({
  85. where: { foo: 'two' },
  86. });
  87. expect(obj0).to.not.be.null;
  88. expect(obj0.foo).to.equal('two');
  89. const obj = await this.RestaurantOne.findOne({
  90. where: { foo: 'two' },
  91. });
  92. expect(obj).to.be.null;
  93. });
  94. });
  95. describe('Get associated data in public schema via include', () => {
  96. beforeEach(async function () {
  97. await Promise.all([
  98. this.LocationOne.sync({ force: true }),
  99. this.LocationTwo.sync({ force: true }),
  100. ]);
  101. await this.LocationTwo.create({ name: 'HQ' });
  102. const obj0 = await this.LocationTwo.findOne({ where: { name: 'HQ' } });
  103. expect(obj0).to.not.be.null;
  104. expect(obj0.name).to.equal('HQ');
  105. locationId = obj0.id;
  106. const obj = await this.LocationOne.findOne({ where: { name: 'HQ' } });
  107. expect(obj).to.be.null;
  108. });
  109. // TODO: fix https://github.com/sequelize/sequelize/issues/17091
  110. it.skip('should be able to insert and retrieve associated data into the table in schema_two', async function () {
  111. await this.RestaurantTwo.create({
  112. foo: 'two',
  113. location_id: locationId,
  114. });
  115. const obj0 = await this.RestaurantTwo.findOne({
  116. where: { foo: 'two' },
  117. include: [
  118. {
  119. model: this.LocationTwo,
  120. as: 'location',
  121. },
  122. ],
  123. });
  124. expect(obj0).to.not.be.null;
  125. expect(obj0.foo).to.equal('two');
  126. expect(obj0.location).to.not.be.null;
  127. expect(obj0.location.name).to.equal('HQ');
  128. const obj = await this.RestaurantOne.findOne({ where: { foo: 'two' } });
  129. expect(obj).to.be.null;
  130. });
  131. });
  132. });
  133. describe('schemas', () => {
  134. beforeEach('build restaurant tables', async function () {
  135. this.Restaurant = current.define(
  136. 'restaurant',
  137. {
  138. foo: DataTypes.STRING,
  139. bar: DataTypes.STRING,
  140. },
  141. { tableName: 'restaurants' },
  142. );
  143. this.Location = current.define(
  144. 'location',
  145. {
  146. name: DataTypes.STRING,
  147. },
  148. { tableName: 'locations' },
  149. );
  150. this.Employee = current.define(
  151. 'employee',
  152. {
  153. first_name: DataTypes.STRING,
  154. last_name: DataTypes.STRING,
  155. },
  156. { tableName: 'employees' },
  157. );
  158. this.Restaurant.belongsTo(this.Location, {
  159. foreignKey: 'location_id',
  160. foreignKeyConstraints: false,
  161. });
  162. this.Employee.belongsTo(this.Restaurant, {
  163. foreignKey: 'restaurant_id',
  164. foreignKeyConstraints: false,
  165. });
  166. this.Restaurant.hasMany(this.Employee, {
  167. foreignKey: 'restaurant_id',
  168. foreignKeyConstraints: false,
  169. });
  170. this.EmployeeOne = this.Employee.withSchema(SCHEMA_ONE);
  171. this.EmployeeTwo = this.Employee.withSchema(SCHEMA_TWO);
  172. this.RestaurantOne = this.Restaurant.withSchema(SCHEMA_ONE);
  173. this.RestaurantTwo = this.Restaurant.withSchema(SCHEMA_TWO);
  174. await Promise.all([current.createSchema(SCHEMA_ONE), current.createSchema(SCHEMA_TWO)]);
  175. await Promise.all([
  176. this.RestaurantOne.sync({ force: true }),
  177. this.RestaurantTwo.sync({ force: true }),
  178. ]);
  179. });
  180. describe('Add data via model.create, retrieve via model.findOne', () => {
  181. it('should be able to insert data into the table in schema_one using create', async function () {
  182. await this.RestaurantOne.create({
  183. foo: 'one',
  184. location_id: locationId,
  185. });
  186. const obj0 = await this.RestaurantOne.findOne({
  187. where: { foo: 'one' },
  188. });
  189. expect(obj0).to.not.be.null;
  190. expect(obj0.foo).to.equal('one');
  191. const restaurantId = obj0.id;
  192. const obj = await this.RestaurantOne.findByPk(restaurantId);
  193. expect(obj).to.not.be.null;
  194. expect(obj.foo).to.equal('one');
  195. const RestaurantObj = await this.RestaurantTwo.findOne({ where: { foo: 'one' } });
  196. expect(RestaurantObj).to.be.null;
  197. });
  198. it('should be able to insert data into the table in schema_two using create', async function () {
  199. await this.RestaurantTwo.create({
  200. foo: 'two',
  201. location_id: locationId,
  202. });
  203. const obj0 = await this.RestaurantTwo.findOne({
  204. where: { foo: 'two' },
  205. });
  206. expect(obj0).to.not.be.null;
  207. expect(obj0.foo).to.equal('two');
  208. const restaurantId = obj0.id;
  209. const obj = await this.RestaurantTwo.findByPk(restaurantId);
  210. expect(obj).to.not.be.null;
  211. expect(obj.foo).to.equal('two');
  212. const RestaurantObj = await this.RestaurantOne.findOne({ where: { foo: 'two' } });
  213. expect(RestaurantObj).to.be.null;
  214. });
  215. });
  216. describe('Persist and retrieve data', () => {
  217. it('should be able to insert data into both schemas using instance.save and retrieve/count it', async function () {
  218. // building and saving in random order to make sure calling
  219. // .schema doesn't impact model prototype
  220. let restaurauntModel = this.RestaurantOne.build({ bar: 'one.1' });
  221. await restaurauntModel.save();
  222. restaurauntModel = this.RestaurantTwo.build({ bar: 'two.1' });
  223. await restaurauntModel.save();
  224. restaurauntModel = this.RestaurantOne.build({ bar: 'one.2' });
  225. await restaurauntModel.save();
  226. restaurauntModel = this.RestaurantTwo.build({ bar: 'two.2' });
  227. await restaurauntModel.save();
  228. restaurauntModel = this.RestaurantTwo.build({ bar: 'two.3' });
  229. await restaurauntModel.save();
  230. const restaurantsOne1 = await this.RestaurantOne.findAll();
  231. expect(restaurantsOne1).to.not.be.null;
  232. expect(restaurantsOne1.length).to.equal(2);
  233. for (const restaurant of restaurantsOne1) {
  234. expect(restaurant.bar).to.contain('one');
  235. }
  236. const restaurantsOne0 = await this.RestaurantOne.findAndCountAll();
  237. expect(restaurantsOne0).to.not.be.null;
  238. expect(restaurantsOne0.rows.length).to.equal(2);
  239. expect(restaurantsOne0.count).to.equal(2);
  240. for (const restaurant of restaurantsOne0.rows) {
  241. expect(restaurant.bar).to.contain('one');
  242. }
  243. const restaurantsOne = await this.RestaurantOne.findAll({
  244. where: { bar: { [Op.like]: '%.1' } },
  245. });
  246. expect(restaurantsOne).to.not.be.null;
  247. expect(restaurantsOne.length).to.equal(1);
  248. for (const restaurant of restaurantsOne) {
  249. expect(restaurant.bar).to.contain('one');
  250. }
  251. const count0 = await this.RestaurantOne.count();
  252. expect(count0).to.not.be.null;
  253. expect(count0).to.equal(2);
  254. const restaurantsTwo1 = await this.RestaurantTwo.findAll();
  255. expect(restaurantsTwo1).to.not.be.null;
  256. expect(restaurantsTwo1.length).to.equal(3);
  257. for (const restaurant of restaurantsTwo1) {
  258. expect(restaurant.bar).to.contain('two');
  259. }
  260. const restaurantsTwo0 = await this.RestaurantTwo.findAndCountAll();
  261. expect(restaurantsTwo0).to.not.be.null;
  262. expect(restaurantsTwo0.rows.length).to.equal(3);
  263. expect(restaurantsTwo0.count).to.equal(3);
  264. for (const restaurant of restaurantsTwo0.rows) {
  265. expect(restaurant.bar).to.contain('two');
  266. }
  267. const restaurantsTwo = await this.RestaurantTwo.findAll({
  268. where: { bar: { [Op.like]: '%.3' } },
  269. });
  270. expect(restaurantsTwo).to.not.be.null;
  271. expect(restaurantsTwo.length).to.equal(1);
  272. for (const restaurant of restaurantsTwo) {
  273. expect(restaurant.bar).to.contain('two');
  274. }
  275. const count = await this.RestaurantTwo.count();
  276. expect(count).to.not.be.null;
  277. expect(count).to.equal(3);
  278. });
  279. });
  280. describe('Get associated data in public schema via include', () => {
  281. beforeEach(async function () {
  282. const Location = this.Location;
  283. await Location.sync({ force: true });
  284. await Location.create({ name: 'HQ' });
  285. const obj = await Location.findOne({ where: { name: 'HQ' } });
  286. expect(obj).to.not.be.null;
  287. expect(obj.name).to.equal('HQ');
  288. locationId = obj.id;
  289. });
  290. it('should be able to insert and retrieve associated data into the table in schema_one', async function () {
  291. await this.RestaurantOne.create({
  292. foo: 'one',
  293. location_id: locationId,
  294. });
  295. const obj = await this.RestaurantOne.findOne({
  296. where: { foo: 'one' },
  297. include: [
  298. {
  299. model: this.Location,
  300. as: 'location',
  301. },
  302. ],
  303. });
  304. expect(obj).to.not.be.null;
  305. expect(obj.foo).to.equal('one');
  306. expect(obj.location).to.not.be.null;
  307. expect(obj.location.name).to.equal('HQ');
  308. });
  309. });
  310. describe('Get schema specific associated data via include', () => {
  311. beforeEach(async function () {
  312. await this.EmployeeOne.sync({ force: true });
  313. await this.EmployeeTwo.sync({ force: true });
  314. });
  315. it('should be able to insert and retrieve associated data into the table in schema_one', async function () {
  316. await this.RestaurantOne.create({
  317. foo: 'one',
  318. });
  319. const obj1 = await this.RestaurantOne.findOne({
  320. where: { foo: 'one' },
  321. });
  322. expect(obj1).to.not.be.null;
  323. expect(obj1.foo).to.equal('one');
  324. const restaurantId = obj1.id;
  325. await this.EmployeeOne.create({
  326. first_name: 'Restaurant',
  327. last_name: 'one',
  328. restaurant_id: restaurantId,
  329. });
  330. const obj0 = await this.RestaurantOne.findOne({
  331. where: { foo: 'one' },
  332. include: [
  333. {
  334. model: this.EmployeeOne,
  335. as: 'employees',
  336. },
  337. ],
  338. });
  339. expect(obj0).to.not.be.null;
  340. expect(obj0.employees).to.not.be.null;
  341. expect(obj0.employees.length).to.equal(1);
  342. expect(obj0.employees[0].last_name).to.equal('one');
  343. const employees = await obj0.getEmployees({ schema: SCHEMA_ONE });
  344. expect(employees.length).to.equal(1);
  345. expect(employees[0].last_name).to.equal('one');
  346. const obj = await this.EmployeeOne.findOne({
  347. where: { last_name: 'one' },
  348. include: [
  349. {
  350. model: this.RestaurantOne,
  351. as: 'restaurant',
  352. },
  353. ],
  354. });
  355. expect(obj).to.not.be.null;
  356. expect(obj.restaurant).to.not.be.null;
  357. expect(obj.restaurant.foo).to.equal('one');
  358. const restaurant = await obj.getRestaurant({ schema: SCHEMA_ONE });
  359. expect(restaurant).to.not.be.null;
  360. expect(restaurant.foo).to.equal('one');
  361. });
  362. it('should be able to insert and retrieve associated data into the table in schema_two', async function () {
  363. await this.RestaurantTwo.create({
  364. foo: 'two',
  365. });
  366. const obj1 = await this.RestaurantTwo.findOne({
  367. where: { foo: 'two' },
  368. });
  369. expect(obj1).to.not.be.null;
  370. expect(obj1.foo).to.equal('two');
  371. const restaurantId = obj1.id;
  372. await this.EmployeeTwo.create({
  373. first_name: 'Restaurant',
  374. last_name: 'two',
  375. restaurant_id: restaurantId,
  376. });
  377. const obj0 = await this.RestaurantTwo.findOne({
  378. where: { foo: 'two' },
  379. include: [
  380. {
  381. model: this.EmployeeTwo,
  382. as: 'employees',
  383. },
  384. ],
  385. });
  386. expect(obj0).to.not.be.null;
  387. expect(obj0.employees).to.not.be.null;
  388. expect(obj0.employees.length).to.equal(1);
  389. expect(obj0.employees[0].last_name).to.equal('two');
  390. const employees = await obj0.getEmployees({ schema: SCHEMA_TWO });
  391. expect(employees.length).to.equal(1);
  392. expect(employees[0].last_name).to.equal('two');
  393. const obj = await this.Employee.withSchema(SCHEMA_TWO).findOne({
  394. where: { last_name: 'two' },
  395. include: [
  396. {
  397. model: this.RestaurantTwo,
  398. as: 'restaurant',
  399. },
  400. ],
  401. });
  402. expect(obj).to.not.be.null;
  403. expect(obj.restaurant).to.not.be.null;
  404. expect(obj.restaurant.foo).to.equal('two');
  405. const restaurant = await obj.getRestaurant({ schema: SCHEMA_TWO });
  406. expect(restaurant).to.not.be.null;
  407. expect(restaurant.foo).to.equal('two');
  408. });
  409. });
  410. describe('concurency tests', () => {
  411. it('should build and persist instances to 2 schemas concurrently in any order', async function () {
  412. const Restaurant = this.Restaurant;
  413. let restaurauntModelSchema1 = Restaurant.withSchema(SCHEMA_ONE).build({ bar: 'one.1' });
  414. const restaurauntModelSchema2 = Restaurant.withSchema(SCHEMA_TWO).build({ bar: 'two.1' });
  415. await restaurauntModelSchema1.save();
  416. restaurauntModelSchema1 = Restaurant.withSchema(SCHEMA_ONE).build({ bar: 'one.2' });
  417. await restaurauntModelSchema2.save();
  418. await restaurauntModelSchema1.save();
  419. const restaurantsOne = await Restaurant.withSchema(SCHEMA_ONE).findAll();
  420. expect(restaurantsOne).to.not.be.null;
  421. expect(restaurantsOne.length).to.equal(2);
  422. for (const restaurant of restaurantsOne) {
  423. expect(restaurant.bar).to.contain('one');
  424. }
  425. const restaurantsTwo = await Restaurant.withSchema(SCHEMA_TWO).findAll();
  426. expect(restaurantsTwo).to.not.be.null;
  427. expect(restaurantsTwo.length).to.equal(1);
  428. for (const restaurant of restaurantsTwo) {
  429. expect(restaurant.bar).to.contain('two');
  430. }
  431. });
  432. });
  433. });
  434. });