query-interface.test.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. 'use strict';
  2. const uniq = require('lodash/uniq');
  3. const chai = require('chai');
  4. const expect = chai.expect;
  5. const Support = require('./support');
  6. const { DataTypes } = require('@sequelize/core');
  7. const dialectName = Support.getTestDialect();
  8. const dialect = Support.sequelize.dialect;
  9. describe('QueryInterface', () => {
  10. beforeEach(function () {
  11. this.queryInterface = this.sequelize.queryInterface;
  12. });
  13. describe('dropAllSchema', () => {
  14. if (!dialect.supports.schemas) {
  15. return;
  16. }
  17. it('should drop all schema', async function () {
  18. await this.queryInterface.dropAllSchemas({
  19. skip: [this.sequelize.options.replication.write.database],
  20. });
  21. const schemaNames = await this.queryInterface.listSchemas();
  22. await this.queryInterface.createSchema('newSchema');
  23. const newSchemaNames = await this.queryInterface.listSchemas();
  24. expect(newSchemaNames).to.have.length(schemaNames.length + 1);
  25. });
  26. });
  27. describe('dropAllTables', () => {
  28. it('should drop all tables', async function () {
  29. await this.queryInterface.dropAllTables();
  30. const tableNames = await this.queryInterface.listTables();
  31. expect(tableNames).to.be.empty;
  32. await this.queryInterface.createTable('table', { name: DataTypes.STRING });
  33. const tableNames1 = await this.queryInterface.listTables();
  34. expect(tableNames1).to.have.length(1);
  35. await this.queryInterface.dropAllTables();
  36. const tableNames2 = await this.queryInterface.listTables();
  37. expect(tableNames2).to.be.empty;
  38. });
  39. it('should be able to skip given tables', async function () {
  40. await this.queryInterface.createTable('skipme', {
  41. name: DataTypes.STRING,
  42. });
  43. await this.queryInterface.dropAllTables({ skip: ['skipme'] });
  44. const result = await this.queryInterface.listTables();
  45. const tableNames = result.map(v => v.tableName);
  46. expect(tableNames).to.contain('skipme');
  47. });
  48. it('should be able to drop a foreign key', async function () {
  49. await this.queryInterface.dropAllTables();
  50. const tableNames = await this.queryInterface.listTables();
  51. expect(tableNames).to.be.empty;
  52. await this.queryInterface.createTable('users', {
  53. id: {
  54. type: DataTypes.INTEGER,
  55. primaryKey: true,
  56. autoIncrement: true,
  57. },
  58. });
  59. await this.queryInterface.createTable('level', {
  60. id: {
  61. type: DataTypes.INTEGER,
  62. primaryKey: true,
  63. autoIncrement: true,
  64. },
  65. });
  66. await this.queryInterface.addColumn('users', 'level_id', {
  67. type: DataTypes.INTEGER,
  68. references: {
  69. table: 'level',
  70. key: 'id',
  71. },
  72. onUpdate: 'cascade',
  73. onDelete: 'set null',
  74. });
  75. const tableNames1 = await this.queryInterface.listTables();
  76. expect(tableNames1).to.have.length(2);
  77. await this.queryInterface.dropAllTables();
  78. const tableNames2 = await this.queryInterface.listTables();
  79. expect(tableNames2).to.be.empty;
  80. });
  81. });
  82. describe('indexes', () => {
  83. beforeEach(async function () {
  84. await this.queryInterface.dropTable('Group');
  85. await this.queryInterface.createTable('Group', {
  86. username: DataTypes.STRING,
  87. isAdmin: DataTypes.BOOLEAN,
  88. from: DataTypes.STRING,
  89. });
  90. });
  91. it('adds, reads and removes an index to the table', async function () {
  92. await this.queryInterface.addIndex('Group', ['username', 'isAdmin']);
  93. let indexes = await this.queryInterface.showIndex('Group');
  94. let indexColumns = uniq(indexes.map(index => index.name));
  95. expect(indexColumns).to.include('group_username_is_admin');
  96. await this.queryInterface.removeIndex('Group', ['username', 'isAdmin']);
  97. indexes = await this.queryInterface.showIndex('Group');
  98. indexColumns = uniq(indexes.map(index => index.name));
  99. expect(indexColumns).to.be.empty;
  100. });
  101. if (dialect.supports.schemas) {
  102. it('works with schemas', async function () {
  103. await this.sequelize.createSchema('schema');
  104. await this.queryInterface.createTable(
  105. 'table',
  106. {
  107. name: {
  108. type: DataTypes.STRING,
  109. },
  110. isAdmin: {
  111. type: DataTypes.STRING,
  112. },
  113. },
  114. {
  115. schema: 'schema',
  116. },
  117. );
  118. await this.queryInterface.addIndex(
  119. { schema: 'schema', tableName: 'table' },
  120. ['name', 'isAdmin'],
  121. null,
  122. 'schema_table',
  123. );
  124. const indexes = await this.queryInterface.showIndex({
  125. schema: 'schema',
  126. tableName: 'table',
  127. });
  128. expect(indexes.length).to.eq(1);
  129. expect(indexes[0].name).to.eq('table_name_is_admin');
  130. });
  131. }
  132. it('does not fail on reserved keywords', async function () {
  133. await this.queryInterface.addIndex('Group', ['from']);
  134. });
  135. });
  136. if (dialectName !== 'ibmi') {
  137. describe('renameColumn', () => {
  138. it('rename a simple column', async function () {
  139. const Users = this.sequelize.define(
  140. '_Users',
  141. {
  142. username: DataTypes.STRING,
  143. },
  144. { freezeTableName: true },
  145. );
  146. await Users.sync({ force: true });
  147. await this.queryInterface.renameColumn('_Users', 'username', 'pseudo');
  148. const table = await this.queryInterface.describeTable('_Users');
  149. expect(table).to.have.property('pseudo');
  150. expect(table).to.not.have.property('username');
  151. });
  152. if (dialect.supports.schemas) {
  153. it('works with schemas', async function () {
  154. await this.sequelize.createSchema('archive');
  155. const Users = this.sequelize.define(
  156. 'User',
  157. {
  158. username: DataTypes.STRING,
  159. },
  160. {
  161. tableName: 'Users',
  162. schema: 'archive',
  163. },
  164. );
  165. await Users.sync({ force: true });
  166. await this.queryInterface.renameColumn(
  167. {
  168. schema: 'archive',
  169. tableName: 'Users',
  170. },
  171. 'username',
  172. 'pseudo',
  173. );
  174. const table = await this.queryInterface.describeTable({
  175. schema: 'archive',
  176. tableName: 'Users',
  177. });
  178. expect(table).to.have.property('pseudo');
  179. expect(table).to.not.have.property('username');
  180. });
  181. }
  182. it('rename a column non-null without default value', async function () {
  183. const Users = this.sequelize.define(
  184. '_Users',
  185. {
  186. username: {
  187. type: DataTypes.STRING,
  188. allowNull: false,
  189. },
  190. },
  191. { freezeTableName: true },
  192. );
  193. await Users.sync({ force: true });
  194. await this.queryInterface.renameColumn('_Users', 'username', 'pseudo');
  195. const table = await this.queryInterface.describeTable('_Users');
  196. expect(table).to.have.property('pseudo');
  197. expect(table).to.not.have.property('username');
  198. });
  199. it('rename a boolean column non-null without default value', async function () {
  200. const Users = this.sequelize.define(
  201. '_Users',
  202. {
  203. active: {
  204. type: DataTypes.BOOLEAN,
  205. allowNull: false,
  206. defaultValue: false,
  207. },
  208. },
  209. { freezeTableName: true },
  210. );
  211. await Users.sync({ force: true });
  212. await this.queryInterface.renameColumn('_Users', 'active', 'enabled');
  213. const table = await this.queryInterface.describeTable('_Users');
  214. expect(table).to.have.property('enabled');
  215. expect(table).to.not.have.property('active');
  216. });
  217. if (dialectName !== 'db2') {
  218. // Db2 does not allow rename of a primary key column
  219. it('renames a column primary key autoIncrement column', async function () {
  220. const Fruits = this.sequelize.define(
  221. 'Fruit',
  222. {
  223. fruitId: {
  224. type: DataTypes.INTEGER,
  225. allowNull: false,
  226. primaryKey: true,
  227. autoIncrement: true,
  228. },
  229. },
  230. { freezeTableName: true },
  231. );
  232. await Fruits.sync({ force: true });
  233. await this.queryInterface.renameColumn('Fruit', 'fruitId', 'fruit_id');
  234. const table = await this.queryInterface.describeTable('Fruit');
  235. expect(table).to.have.property('fruit_id');
  236. expect(table).to.not.have.property('fruitId');
  237. });
  238. }
  239. it('shows a reasonable error message when column is missing', async function () {
  240. const Users = this.sequelize.define(
  241. '_Users',
  242. {
  243. username: DataTypes.STRING,
  244. },
  245. { freezeTableName: true },
  246. );
  247. await Users.sync({ force: true });
  248. await expect(
  249. this.queryInterface.renameColumn('_Users', 'email', 'pseudo'),
  250. ).to.be.rejectedWith("Table _Users doesn't have the column email");
  251. });
  252. });
  253. }
  254. describe('addColumn', () => {
  255. beforeEach(async function () {
  256. await this.queryInterface.createTable('users', {
  257. id: {
  258. type: DataTypes.INTEGER,
  259. primaryKey: true,
  260. autoIncrement: true,
  261. },
  262. });
  263. });
  264. it('should be able to add a foreign key reference', async function () {
  265. await this.queryInterface.createTable('level', {
  266. id: {
  267. type: DataTypes.INTEGER,
  268. primaryKey: true,
  269. autoIncrement: true,
  270. },
  271. });
  272. await this.queryInterface.addColumn('users', 'level_id', {
  273. type: DataTypes.INTEGER,
  274. references: {
  275. table: 'level',
  276. key: 'id',
  277. },
  278. onUpdate: 'cascade',
  279. onDelete: 'set null',
  280. });
  281. const table = await this.queryInterface.describeTable('users');
  282. expect(table).to.have.property('level_id');
  283. });
  284. it('addColumn expected error', async function () {
  285. await this.queryInterface.createTable('level2', {
  286. id: {
  287. type: DataTypes.INTEGER,
  288. primaryKey: true,
  289. autoIncrement: true,
  290. },
  291. });
  292. const testArgs = (...args) =>
  293. expect(this.queryInterface.addColumn(...args)).to.be.rejectedWith(
  294. Error,
  295. 'addColumn takes at least 3 arguments (table, attribute name, attribute definition)',
  296. );
  297. await testArgs('users', 'level_id');
  298. await testArgs(null, 'level_id');
  299. await testArgs('users', null, {});
  300. });
  301. if (dialect.supports.schemas) {
  302. it('should work with schemas', async function () {
  303. await this.sequelize.createSchema('archive');
  304. await this.queryInterface.createTable(
  305. { tableName: 'users', schema: 'archive' },
  306. {
  307. id: {
  308. type: DataTypes.INTEGER,
  309. primaryKey: true,
  310. autoIncrement: true,
  311. },
  312. },
  313. );
  314. await this.queryInterface.addColumn({ tableName: 'users', schema: 'archive' }, 'level_id', {
  315. type: DataTypes.INTEGER,
  316. });
  317. const table = await this.queryInterface.describeTable({
  318. tableName: 'users',
  319. schema: 'archive',
  320. });
  321. expect(table).to.have.property('level_id');
  322. });
  323. }
  324. // Db2 does not support enums in alter column
  325. if (dialectName !== 'db2') {
  326. it('should work with enums (1)', async function () {
  327. await this.queryInterface.addColumn(
  328. 'users',
  329. 'someEnum',
  330. DataTypes.ENUM('value1', 'value2', 'value3'),
  331. );
  332. });
  333. it('should work with enums (2)', async function () {
  334. await this.queryInterface.addColumn('users', 'someOtherEnum', {
  335. type: DataTypes.ENUM(['value1', 'value2', 'value3']),
  336. });
  337. });
  338. }
  339. if (dialectName === 'postgres') {
  340. it('should be able to add a column of type of array of enums', async function () {
  341. await this.queryInterface.addColumn('users', 'tags', {
  342. allowNull: false,
  343. type: DataTypes.ARRAY(DataTypes.ENUM('Value1', 'Value2', 'Value3')),
  344. });
  345. const result = await this.queryInterface.describeTable('users');
  346. expect(result).to.have.property('tags');
  347. expect(result.tags.type).to.equal('ARRAY');
  348. expect(result.tags.allowNull).to.be.false;
  349. });
  350. }
  351. });
  352. });