123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- 'use strict';
- const chai = require('chai');
- const expect = chai.expect;
- const Support = require('../../support');
- const { DataTypes, Op, Sequelize } = require('@sequelize/core');
- const {
- _validateIncludedElements,
- } = require('@sequelize/core/_non-semver-use-at-your-own-risk_/model-internals.js');
- const current = Support.sequelize;
- describe(Support.getTestDialectTeaser('Model'), () => {
- describe('all', () => {
- it('can expand nested self-reference', () => {
- const Referral = current.define('referral');
- Referral.belongsTo(Referral);
- const options = { include: [{ all: true, nested: true }] };
- Referral._expandIncludeAll(options, Referral);
- expect(options.include).to.deep.equal([
- { as: 'referral', model: Referral, association: Referral.associations.referral },
- ]);
- });
- });
- describe('_validateIncludedElements', () => {
- beforeEach(function () {
- this.User = this.sequelize.define('User');
- this.Task = this.sequelize.define('Task', {
- title: DataTypes.STRING,
- });
- this.Company = this.sequelize.define('Company', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- field: 'field_id',
- },
- name: DataTypes.STRING,
- });
- this.User.Tasks = this.User.hasMany(this.Task);
- this.User.Company = this.User.belongsTo(this.Company);
- this.Company.Employees = this.Company.hasMany(this.User);
- this.Company.Owner = this.Company.belongsTo(this.User, {
- as: 'Owner',
- foreignKey: 'ownerId',
- });
- });
- describe('attributes', () => {
- it("should not inject the aliased PK again, if it's already there", function () {
- let options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- model: this.Company,
- attributes: ['name'],
- },
- ],
- });
- expect(options.include[0].attributes).to.deep.equal([['field_id', 'id'], 'name']);
- options = _validateIncludedElements(options);
- // Calling validate again shouldn't add the pk again
- expect(options.include[0].attributes).to.deep.equal([['field_id', 'id'], 'name']);
- });
- describe('include / exclude', () => {
- it('allows me to include additional attributes', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- model: this.Company,
- attributes: {
- include: ['foobar'],
- },
- },
- ],
- });
- expect(options.include[0].attributes).to.deep.equal([
- ['field_id', 'id'],
- 'name',
- 'createdAt',
- 'updatedAt',
- 'ownerId',
- 'foobar',
- ]);
- });
- it('allows me to exclude attributes', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- model: this.Company,
- attributes: {
- exclude: ['name'],
- },
- },
- ],
- });
- expect(options.include[0].attributes).to.deep.equal([
- ['field_id', 'id'],
- 'createdAt',
- 'updatedAt',
- 'ownerId',
- ]);
- });
- it('include takes precendence over exclude', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- model: this.Company,
- attributes: {
- exclude: ['name'],
- include: ['name'],
- },
- },
- ],
- });
- expect(options.include[0].attributes).to.deep.equal([
- ['field_id', 'id'],
- 'createdAt',
- 'updatedAt',
- 'ownerId',
- 'name',
- ]);
- });
- });
- });
- describe('scope', () => {
- beforeEach(function () {
- this.Project = this.sequelize.define(
- 'project',
- {
- bar: {
- type: DataTypes.STRING,
- field: 'foo',
- },
- },
- {
- defaultScope: {
- where: {
- active: true,
- },
- },
- scopes: {
- this: {
- where: { this: true },
- },
- that: {
- where: { that: false },
- limit: 12,
- },
- attr: {
- attributes: ['baz'],
- },
- foobar: {
- where: {
- bar: 42,
- },
- },
- },
- },
- );
- this.User.hasMany(this.Project);
- this.User.hasMany(this.Project.withScope('this'), { as: 'thisProject' });
- });
- it('adds the default scope to where', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ model: this.Project, as: 'projects' }],
- });
- expect(options.include[0]).to.have.property('where').which.deep.equals({ active: true });
- });
- it('adds the where from a scoped model', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ model: this.Project.withScope('that'), as: 'projects' }],
- });
- expect(options.include[0]).to.have.property('where').which.deep.equals({ that: false });
- expect(options.include[0]).to.have.property('limit').which.equals(12);
- });
- it('adds the attributes from a scoped model', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ model: this.Project.withScope('attr'), as: 'projects' }],
- });
- expect(options.include[0]).to.have.property('attributes').which.deep.equals(['baz']);
- });
- it('merges where with the where from a scoped model', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- { where: { active: false }, model: this.Project.withScope('that'), as: 'projects' },
- ],
- });
- expect(options.include[0].where).to.deep.equal({
- [Op.and]: [{ that: false }, { active: false }],
- });
- });
- it('add the where from a scoped associated model', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ model: this.Project, as: 'thisProject' }],
- });
- expect(options.include[0]).to.have.property('where').which.deep.equals({ this: true });
- });
- it('handles a scope with an aliased column (.field)', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ model: this.Project.withScope('foobar'), as: 'projects' }],
- });
- expect(options.include[0]).to.have.property('where').which.deep.equals({ bar: 42 });
- });
- });
- describe('duplicating', () => {
- it('should tag a hasMany association as duplicating: true if undefined', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [this.User.Tasks],
- });
- expect(options.include[0].duplicating).to.equal(true);
- });
- it('should respect include.duplicating for a hasMany', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks, duplicating: false }],
- });
- expect(options.include[0].duplicating).to.equal(false);
- });
- });
- describe('_conformInclude', () => {
- it('should expand association from string alias', function () {
- const options = {
- include: ['Owner'],
- };
- Sequelize.Model._conformIncludes(options, this.Company);
- expect(options.include[0]).to.deep.equal({
- model: this.User,
- association: this.Company.Owner,
- as: 'Owner',
- });
- });
- it('should expand string association', function () {
- const options = {
- include: [
- {
- association: 'Owner',
- attributes: ['id'],
- },
- ],
- };
- Sequelize.Model._conformIncludes(options, this.Company);
- expect(options.include[0]).to.deep.equal({
- model: this.User,
- association: this.Company.Owner,
- attributes: ['id'],
- as: 'Owner',
- });
- });
- it('should throw an error if invalid model is passed', function () {
- const options = {
- include: [
- {
- model: null,
- },
- ],
- };
- expect(() => {
- Sequelize.Model._conformIncludes(options, this.Company);
- }).to.throw(
- 'Invalid Include received. Include has to be either a Model, an Association, the name of an association, or a plain object compatible with IncludeOptions.',
- );
- });
- it('should throw an error if invalid association is passed', function () {
- const options = {
- include: [
- {
- association: null,
- },
- ],
- };
- expect(() => {
- Sequelize.Model._conformIncludes(options, this.Company);
- }).to.throw(
- 'Invalid Include received. Include has to be either a Model, an Association, the name of an association, or a plain object compatible with IncludeOptions.',
- );
- });
- });
- describe('getAssociationWithModel', () => {
- it('returns an association when there is a single unaliased association', function () {
- expect(this.User.getAssociationWithModel(this.Task)).to.equal(this.User.Tasks);
- });
- it('returns an association when there is a single aliased association', function () {
- const User = this.sequelize.define('User');
- const Task = this.sequelize.define('Task');
- const Tasks = Task.belongsTo(User, { as: 'owner' });
- expect(Task.getAssociationWithModel(User, 'owner')).to.equal(Tasks);
- });
- it('returns an association when there are multiple aliased associations', function () {
- expect(this.Company.getAssociationWithModel(this.User, 'Owner')).to.equal(
- this.Company.Owner,
- );
- });
- });
- describe('subQuery', () => {
- it('should be true if theres a duplicating association', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks }],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- });
- it('should be false if theres a duplicating association but no limit', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks }],
- limit: null,
- });
- expect(options.subQuery).to.equal(false);
- });
- it('should be true if theres a nested duplicating association', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- association: this.User.Company,
- include: [this.Company.Employees],
- },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- });
- it('should be false if theres a nested duplicating association but no limit', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- association: this.User.Company,
- include: [this.Company.Employees],
- },
- ],
- limit: null,
- });
- expect(options.subQuery).to.equal(false);
- });
- it('should tag a required hasMany association', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks, required: true }],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- expect(options.include[0].subQuery).to.equal(false);
- expect(options.include[0].subQueryFilter).to.equal(true);
- });
- it('should not tag a required hasMany association with duplicating false', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks, required: true, duplicating: false }],
- limit: 3,
- });
- expect(options.subQuery).to.equal(false);
- expect(options.include[0].subQuery).to.equal(false);
- expect(options.include[0].subQueryFilter).to.equal(false);
- });
- it('should not tag a separate hasMany association with subQuery true', function () {
- const options = _validateIncludedElements({
- model: this.Company,
- include: [
- {
- association: this.Company.Employees,
- separate: true,
- include: [{ association: this.User.Tasks, required: true }],
- },
- ],
- required: true,
- });
- expect(options.subQuery).to.equal(false);
- expect(options.include[0].subQuery).to.equal(false);
- expect(options.include[0].subQueryFilter).to.equal(false);
- });
- it('should tag a hasMany association with where', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [{ association: this.User.Tasks, where: { title: Math.random().toString() } }],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- expect(options.include[0].subQuery).to.equal(false);
- expect(options.include[0].subQueryFilter).to.equal(true);
- });
- it('should not tag a hasMany association with where and duplicating false', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- association: this.User.Tasks,
- where: { title: Math.random().toString() },
- duplicating: false,
- },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(false);
- expect(options.include[0].subQuery).to.equal(false);
- expect(options.include[0].subQueryFilter).to.equal(false);
- });
- it('should tag a required belongsTo alongside a duplicating association', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- { association: this.User.Company, required: true },
- { association: this.User.Tasks },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- expect(options.include[0].subQuery).to.equal(true);
- });
- it('should not tag a required belongsTo alongside a duplicating association with duplicating false', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- { association: this.User.Company, required: true },
- { association: this.User.Tasks, duplicating: false },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(false);
- expect(options.include[0].subQuery).to.equal(false);
- });
- it('should tag a belongsTo association with where alongside a duplicating association', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- { association: this.User.Company, where: { name: Math.random().toString() } },
- { association: this.User.Tasks },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- expect(options.include[0].subQuery).to.equal(true);
- });
- it('should tag a required belongsTo association alongside a duplicating association with a nested belongsTo', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- {
- association: this.User.Company,
- required: true,
- include: [this.Company.Owner],
- },
- this.User.Tasks,
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(true);
- expect(options.include[0].subQuery).to.equal(true);
- expect(options.include[0].include[0].subQuery).to.equal(false);
- expect(options.include[0].include[0].parent.subQuery).to.equal(true);
- });
- it('should tag a belongsTo association with where alongside a duplicating association with duplicating false', function () {
- const options = _validateIncludedElements({
- model: this.User,
- include: [
- { association: this.User.Company, where: { name: Math.random().toString() } },
- { association: this.User.Tasks, duplicating: false },
- ],
- limit: 3,
- });
- expect(options.subQuery).to.equal(false);
- expect(options.include[0].subQuery).to.equal(false);
- });
- });
- });
- });
|