123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- 'use strict';
- const chai = require('chai');
- const sinon = require('sinon');
- const expect = chai.expect;
- const Support = require('../../support');
- const { DataTypes, Sequelize } = require('@sequelize/core');
- const dialect = Support.getTestDialect();
- describe(Support.getTestDialectTeaser('Model'), () => {
- before(function () {
- this.clock = sinon.useFakeTimers();
- });
- after(function () {
- this.clock.restore();
- });
- describe('attributes', () => {
- describe('field', () => {
- beforeEach(async function () {
- const queryInterface = this.sequelize.queryInterface;
- this.User = this.sequelize.define(
- 'user',
- {
- id: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- field: 'userId',
- },
- name: {
- type: DataTypes.STRING,
- field: 'full_name',
- },
- taskCount: {
- type: DataTypes.INTEGER,
- field: 'task_count',
- defaultValue: 0,
- allowNull: false,
- },
- },
- {
- tableName: 'users',
- timestamps: false,
- },
- );
- this.Task = this.sequelize.define(
- 'task',
- {
- id: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- field: 'taskId',
- },
- title: {
- type: DataTypes.STRING,
- field: 'name',
- },
- },
- {
- tableName: 'tasks',
- timestamps: false,
- },
- );
- this.Comment = this.sequelize.define(
- 'comment',
- {
- id: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- field: 'commentId',
- },
- text: { type: DataTypes.STRING, field: 'comment_text' },
- notes: { type: DataTypes.STRING, field: 'notes' },
- likes: { type: DataTypes.INTEGER, field: 'like_count' },
- createdAt: { field: 'created_at' },
- updatedAt: { field: 'updated_at' },
- },
- {
- tableName: 'comments',
- timestamps: true,
- },
- );
- this.User.hasMany(this.Task, {
- foreignKey: 'user_id',
- });
- this.Task.belongsTo(this.User, {
- foreignKey: 'user_id',
- });
- this.Task.hasMany(this.Comment, {
- foreignKey: 'task_id',
- });
- this.Comment.belongsTo(this.Task, {
- foreignKey: 'task_id',
- });
- this.User.belongsToMany(this.Comment, {
- foreignKey: 'userId',
- otherKey: 'commentId',
- through: 'userComments',
- });
- await Promise.all([
- queryInterface.createTable('users', {
- userId: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- },
- full_name: {
- type: DataTypes.STRING,
- },
- task_count: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 0,
- },
- }),
- queryInterface.createTable('tasks', {
- taskId: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- },
- user_id: {
- type: DataTypes.INTEGER,
- },
- name: {
- type: DataTypes.STRING,
- },
- }),
- queryInterface.createTable('comments', {
- commentId: {
- type: DataTypes.INTEGER,
- allowNull: false,
- primaryKey: true,
- autoIncrement: true,
- },
- task_id: {
- type: DataTypes.INTEGER,
- },
- comment_text: {
- type: DataTypes.STRING,
- },
- notes: {
- type: DataTypes.STRING,
- },
- like_count: {
- type: DataTypes.INTEGER,
- },
- created_at: {
- type: DataTypes.DATE,
- allowNull: false,
- },
- updated_at: {
- type: DataTypes.DATE,
- },
- }),
- queryInterface.createTable('userComments', {
- commentId: {
- type: DataTypes.INTEGER,
- },
- userId: {
- type: DataTypes.INTEGER,
- },
- }),
- ]);
- });
- describe('primaryKey', () => {
- describe('in combination with allowNull', () => {
- beforeEach(async function () {
- this.ModelUnderTest = this.sequelize.define('ModelUnderTest', {
- identifier: {
- primaryKey: true,
- type: DataTypes.STRING,
- allowNull: false,
- },
- });
- await this.ModelUnderTest.sync({ force: true });
- });
- it('sets the column to not allow null', async function () {
- const fields = await this.ModelUnderTest.describe();
- expect(fields.identifier).to.include({ allowNull: false });
- });
- });
- it('should support instance.destroy()', async function () {
- const user = await this.User.create();
- await user.destroy();
- });
- it('should support Model.destroy()', async function () {
- const user = await this.User.create();
- await this.User.destroy({
- where: {
- id: user.get('id'),
- },
- });
- });
- });
- describe('field and attribute name is the same', () => {
- beforeEach(async function () {
- await this.Comment.bulkCreate([{ notes: 'Number one' }, { notes: 'Number two' }]);
- });
- it('bulkCreate should work', async function () {
- const comments = await this.Comment.findAll();
- expect(comments[0].notes).to.equal('Number one');
- expect(comments[1].notes).to.equal('Number two');
- });
- it('find with where should work', async function () {
- const comments = await this.Comment.findAll({ where: { notes: 'Number one' } });
- expect(comments).to.have.length(1);
- expect(comments[0].notes).to.equal('Number one');
- });
- it('reload should work', async function () {
- const comment = await this.Comment.findByPk(1);
- await comment.reload();
- });
- it('save should work', async function () {
- const comment1 = await this.Comment.create({ notes: 'my note' });
- comment1.notes = 'new note';
- const comment0 = await comment1.save();
- const comment = await comment0.reload();
- expect(comment.notes).to.equal('new note');
- });
- });
- it('increment should work', async function () {
- await this.Comment.truncate();
- const comment1 = await this.Comment.create({ note: 'oh boy, here I go again', likes: 23 });
- const comment0 = await comment1.increment('likes');
- const comment = await comment0.reload();
- expect(comment.likes).to.equal(24);
- });
- it('decrement should work', async function () {
- await this.Comment.truncate();
- const comment1 = await this.Comment.create({ note: 'oh boy, here I go again', likes: 23 });
- const comment0 = await comment1.decrement('likes');
- const comment = await comment0.reload();
- expect(comment.likes).to.equal(22);
- });
- it('sum should work', async function () {
- await this.Comment.truncate();
- await this.Comment.create({ note: 'oh boy, here I go again', likes: 23 });
- const likes = await this.Comment.sum('likes');
- expect(likes).to.equal(23);
- });
- it('should create, fetch and update with alternative field names from a simple model', async function () {
- await this.User.create({
- name: 'Foobar',
- });
- const user0 = await this.User.findOne({
- limit: 1,
- });
- expect(user0.get('name')).to.equal('Foobar');
- await user0.update({
- name: 'Barfoo',
- });
- const user = await this.User.findOne({
- limit: 1,
- });
- expect(user.get('name')).to.equal('Barfoo');
- });
- it('should bulk update', async function () {
- const Entity = this.sequelize.define('Entity', {
- strField: { type: DataTypes.STRING, field: 'str_field' },
- });
- await this.sequelize.sync({ force: true });
- await Entity.create({ strField: 'foo' });
- await Entity.update({ strField: 'bar' }, { where: { strField: 'foo' } });
- const entity = await Entity.findOne({
- where: {
- strField: 'bar',
- },
- });
- expect(entity).to.be.ok;
- expect(entity.get('strField')).to.equal('bar');
- });
- it('should not contain the field properties after create', async function () {
- const Model = this.sequelize.define(
- 'test',
- {
- id: {
- type: DataTypes.INTEGER,
- field: 'test_id',
- autoIncrement: true,
- primaryKey: true,
- validate: {
- min: 1,
- },
- },
- title: {
- allowNull: false,
- type: DataTypes.STRING(255),
- field: 'test_title',
- },
- },
- {
- timestamps: true,
- underscored: true,
- freezeTableName: true,
- },
- );
- await Model.sync({ force: true });
- const data = await Model.create({ title: 'test' });
- expect(data.get('test_title')).to.be.an('undefined');
- expect(data.get('test_id')).to.be.an('undefined');
- });
- it('should make the aliased auto incremented primary key available after create', async function () {
- const user = await this.User.create({
- name: 'Barfoo',
- });
- expect(user.get('id')).to.be.ok;
- });
- it('should work with where on includes for find', async function () {
- const user = await this.User.create({
- name: 'Barfoo',
- });
- const task0 = await user.createTask({
- title: 'DatDo',
- });
- await task0.createComment({
- text: 'Comment',
- });
- const task = await this.Task.findOne({
- include: [{ model: this.Comment }, { model: this.User }],
- where: { title: 'DatDo' },
- });
- expect(task.get('title')).to.equal('DatDo');
- expect(task.get('comments')[0].get('text')).to.equal('Comment');
- expect(task.get('user')).to.be.ok;
- });
- it('should work with where on includes for findAll', async function () {
- const user = await this.User.create({
- name: 'Foobar',
- });
- const task = await user.createTask({
- title: 'DoDat',
- });
- await task.createComment({
- text: 'Comment',
- });
- const users = await this.User.findAll({
- include: [
- {
- model: this.Task,
- where: { title: 'DoDat' },
- include: [{ model: this.Comment }],
- },
- ],
- });
- for (const user of users) {
- expect(user.get('name')).to.be.ok;
- expect(user.get('tasks')[0].get('title')).to.equal('DoDat');
- expect(user.get('tasks')[0].get('comments')).to.be.ok;
- }
- });
- it('should work with increment', async function () {
- const user = await this.User.create();
- await user.increment('taskCount');
- });
- it('should work with a simple where', async function () {
- await this.User.create({
- name: 'Foobar',
- });
- const user = await this.User.findOne({
- where: {
- name: 'Foobar',
- },
- });
- expect(user).to.be.ok;
- });
- it('should work with a where or', async function () {
- await this.User.create({
- name: 'Foobar',
- });
- const user = await this.User.findOne({
- where: this.sequelize.or(
- {
- name: 'Foobar',
- },
- {
- name: 'Lollerskates',
- },
- ),
- });
- expect(user).to.be.ok;
- });
- it('should work with bulkCreate and findAll', async function () {
- await this.User.bulkCreate([
- {
- name: 'Abc',
- },
- {
- name: 'Bcd',
- },
- {
- name: 'Cde',
- },
- ]);
- const users = await this.User.findAll();
- for (const user of users) {
- expect(['Abc', 'Bcd', 'Cde'].includes(user.get('name'))).to.be.true;
- }
- });
- it('should support renaming of sequelize method fields', async function () {
- const Test = this.sequelize.define('test', {
- someProperty: DataTypes.VIRTUAL, // Since we specify the AS part as a part of the literal string, not with sequelize syntax, we have to tell sequelize about the field
- });
- await this.sequelize.sync({ force: true });
- await Test.create({});
- let findAttributes;
- if (dialect === 'mssql') {
- findAttributes = [
- Sequelize.literal(
- 'CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT) AS "someProperty"',
- ),
- [
- Sequelize.literal('CAST(CASE WHEN EXISTS(SELECT 1) THEN 1 ELSE 0 END AS BIT)'),
- 'someProperty2',
- ],
- ];
- } else if (['db2', 'ibmi'].includes(dialect)) {
- findAttributes = [
- Sequelize.literal('1 AS "someProperty"'),
- [Sequelize.literal('1'), 'someProperty2'],
- ];
- } else {
- findAttributes = [
- Sequelize.literal('EXISTS(SELECT 1) AS "someProperty"'),
- [Sequelize.literal('EXISTS(SELECT 1)'), 'someProperty2'],
- ];
- }
- const tests = await Test.findAll({
- attributes: findAttributes,
- });
- expect(tests[0].get('someProperty')).to.be.ok;
- expect(tests[0].get('someProperty2')).to.be.ok;
- });
- it('should sync foreign keys with custom field names', async function () {
- await this.sequelize.sync({ force: true });
- const attrs = this.Task.tableAttributes;
- expect(attrs.user_id.references.table).to.deep.equal({
- tableName: 'users',
- schema: this.sequelize.dialect.getDefaultSchema(),
- delimiter: '.',
- });
- expect(attrs.user_id.references.key).to.equal('userId');
- });
- it('should find the value of an attribute with a custom field name', async function () {
- await this.User.create({ name: 'test user' });
- const user = await this.User.findOne({ where: { name: 'test user' } });
- expect(user.name).to.equal('test user');
- });
- it('field names that are the same as property names should create, update, and read correctly', async function () {
- await this.Comment.create({
- notes: 'Foobar',
- });
- const comment0 = await this.Comment.findOne({
- limit: 1,
- });
- expect(comment0.get('notes')).to.equal('Foobar');
- await comment0.update({
- notes: 'Barfoo',
- });
- const comment = await this.Comment.findOne({
- limit: 1,
- });
- expect(comment.get('notes')).to.equal('Barfoo');
- });
- it('should work with a belongsTo association getter', async function () {
- const userId = Math.floor(Math.random() * 100_000);
- const [user, task] = await Promise.all([
- this.User.create({
- id: userId,
- }),
- this.Task.create({
- user_id: userId,
- }),
- ]);
- const [userA, userB] = await Promise.all([user, task.getUser()]);
- expect(userA.get('id')).to.equal(userB.get('id'));
- expect(userA.get('id')).to.equal(userId);
- expect(userB.get('id')).to.equal(userId);
- });
- it('should work with paranoid instance.destroy()', async function () {
- const User = this.sequelize.define(
- 'User',
- {
- deletedAt: {
- type: DataTypes.DATE,
- field: 'deleted_at',
- },
- },
- {
- timestamps: true,
- paranoid: true,
- },
- );
- await User.sync({ force: true });
- const user = await User.create();
- await user.destroy();
- this.clock.tick(1000);
- const users = await User.findAll();
- expect(users.length).to.equal(0);
- });
- it('should work with paranoid Model.destroy()', async function () {
- const User = this.sequelize.define(
- 'User',
- {
- deletedAt: {
- type: DataTypes.DATE,
- field: 'deleted_at',
- },
- },
- {
- timestamps: true,
- paranoid: true,
- },
- );
- await User.sync({ force: true });
- const user = await User.create();
- await User.destroy({ where: { id: user.get('id') } });
- const users = await User.findAll();
- expect(users.length).to.equal(0);
- });
- it('should work with `belongsToMany` association `count`', async function () {
- const user = await this.User.create({
- name: 'John',
- });
- const commentCount = await user.countComments();
- await expect(commentCount).to.equal(0);
- });
- it('should work with `hasMany` association `count`', async function () {
- const user = await this.User.create({
- name: 'John',
- });
- const taskCount = await user.countTasks();
- await expect(taskCount).to.equal(0);
- });
- });
- });
- });
|