'use strict'; const { DataTypes } = require('@sequelize/core'); const { AbstractQuery: Query, } = require('@sequelize/core/_non-semver-use-at-your-own-risk_/abstract-dialect/query.js'); const Support = require('../../../support'); const chai = require('chai'); const { match, stub } = require('sinon'); const current = Support.sequelize; const expect = chai.expect; describe('[ABSTRACT]', () => { describe('_groupJoinData', () => { it('should hash second nested set correctly, when has multiple primary keys and one is a Buffer', () => { const Team = current.define('team', { id: { primaryKey: true, type: DataTypes.STRING(1), }, name: { type: DataTypes.TEXT, }, }); const Player = current.define('player', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Agent = current.define('agent', { uuid: { primaryKey: true, type: 'BINARY(16)', }, id: { primaryKey: true, type: DataTypes.STRING(1), }, }); Team.Player = Team.hasMany(Player, { foreignKey: 'teamId' }); Team.Agent = Team.hasMany(Agent, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association: Team.Player, }, agents: { model: Agent, association: Team.Agent, }, }, }; const agentOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const agentTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { id: 'a', 'players.id': '1-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), 'agents.uuid': agentOneUuid, name: 'vansh', 'agents.id': 'p', 'agents.name': 'One', }, { id: 'a', 'players.id': '2-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), 'agents.uuid': agentTwoUuid, name: 'joe', 'agents.id': 'z', 'agents.name': 'Two', }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(1); expect(result[0]).to.have.property('id').and.be.equal('a'); expect(result[0]).to.have.property('name').and.be.ok; expect(result[0].agents).to.be.deep.equal([ { id: 'p', uuid: agentOneUuid, name: 'One', }, { id: 'z', uuid: agentTwoUuid, name: 'Two', }, ]); }); it('should hash second nested set correctly, when primary is a Buffer', () => { const Team = current.define('team', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Player = current.define('player', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Agent = current.define('agent', { uuid: { primaryKey: true, type: 'BINARY(16)', }, }); Team.Player = Team.hasMany(Player, { foreignKey: 'teamId' }); Team.Agent = Team.hasMany(Agent, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association: Team.Player, }, agents: { model: Agent, association: Team.Agent, }, }, }; const agentOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const agentTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { id: 'a', 'players.id': '1-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), 'agents.uuid': agentOneUuid, 'agents.name': 'One', }, { id: 'a', 'players.id': '2-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), 'agents.uuid': agentTwoUuid, 'agents.name': 'Two', }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(1); expect(result[0]).to.have.property('id').and.be.equal('a'); expect(result[0].agents).to.be.deep.equal([ { uuid: agentOneUuid, name: 'One', }, { uuid: agentTwoUuid, name: 'Two', }, ]); }); it('should hash parents correctly, when has multiple primary keys and one is a Buffer', () => { const Team = current.define('team', { uuid: { primaryKey: true, type: 'BINARY(16)', }, id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Player = current.define('player', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const association = Team.hasMany(Player, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association, }, }, }; const teamOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const teamTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { uuid: teamOneUuid, id: 'x', 'players.id': '1-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), }, { uuid: teamTwoUuid, id: 'y', 'players.id': '2-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), }, { uuid: teamOneUuid, id: 'x', 'players.id': '1-2', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-24T11:16:44.000Z'), }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(2); expect(result[0]).to.have.property('uuid').and.be.equal(teamOneUuid); expect(result[0].players).to.be.deep.equal([ { id: '1-1', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-03-06T15:47:30.000Z'), }, { id: '1-2', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-24T11:16:44.000Z'), }, ]); expect(result[1]).to.have.property('uuid').and.be.equal(teamTwoUuid); expect(result[1].players).to.be.deep.equal([ { id: '2-1', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-22T11:16:44.000Z'), }, ]); }); it('should hash parents correctly, when primary key is a Buffer', () => { const Team = current.define('team', { uuid: { primaryKey: true, type: 'BINARY(16)', }, }); const Player = current.define('player', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const association = Team.hasMany(Player, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association, }, }, }; const teamOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const teamTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { uuid: teamOneUuid, 'players.id': '1-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), }, { uuid: teamTwoUuid, 'players.id': '2-1', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), }, { uuid: teamOneUuid, 'players.id': '1-2', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-24T11:16:44.000Z'), }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(2); expect(result[0]).to.have.property('uuid').and.be.equal(teamOneUuid); expect(result[0].players).to.be.deep.equal([ { id: '1-1', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-03-06T15:47:30.000Z'), }, { id: '1-2', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-24T11:16:44.000Z'), }, ]); expect(result[1]).to.have.property('uuid').and.be.equal(teamTwoUuid); expect(result[1].players).to.be.deep.equal([ { id: '2-1', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-22T11:16:44.000Z'), }, ]); }); it('should hash nested correctly, when primary key is a Buffer', () => { const Team = current.define('team', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Player = current.define('player', { uuid: { primaryKey: true, type: 'BINARY(16)', }, }); const association = Team.hasMany(Player, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association, }, }, }; const playerOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const playerTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { id: '1', 'players.uuid': playerOneUuid, 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), }, { id: '1', 'players.uuid': playerTwoUuid, 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(1); expect(result[0]).to.have.property('id').and.be.equal('1'); expect(result[0].players).to.be.deep.equal([ { uuid: playerOneUuid, created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-03-06T15:47:30.000Z'), }, { uuid: playerTwoUuid, created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-22T11:16:44.000Z'), }, ]); }); it('should hash nested correctly, when has multiple primary keys and one is a Buffer', () => { const Team = current.define('team', { id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const Player = current.define('player', { uuid: { primaryKey: true, type: 'BINARY(16)', }, id: { primaryKey: true, type: DataTypes.STRING(1), }, }); const association = Team.hasMany(Player, { foreignKey: 'teamId' }); const includeOptions = { model: Team, includeMap: { players: { model: Player, association, }, }, }; const playerOneUuid = Buffer.from('966ea4c3028c11e7bc99a99d4c0d78cf', 'hex'); const playerTwoUuid = Buffer.from('966ecbd0028c11e7bc99a99d4c0d78cf', 'hex'); const data = [ { id: '1', 'players.uuid': playerOneUuid, 'players.id': 'x', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-03-06T15:47:30.000Z'), }, { id: '1', 'players.uuid': playerTwoUuid, 'players.id': 'y', 'players.created': new Date('2017-03-06T15:47:30.000Z'), 'players.lastModified': new Date('2017-08-22T11:16:44.000Z'), }, ]; const result = Query._groupJoinData(data, includeOptions, { checkExisting: true }); expect(result.length).to.equal(1); expect(result[0]).to.have.property('id').and.be.equal('1'); expect(result[0].players).to.be.deep.equal([ { uuid: playerOneUuid, id: 'x', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-03-06T15:47:30.000Z'), }, { uuid: playerTwoUuid, id: 'y', created: new Date('2017-03-06T15:47:30.000Z'), lastModified: new Date('2017-08-22T11:16:44.000Z'), }, ]); }); }); describe('_logQuery', () => { beforeEach(function () { this.cls = class MyQuery extends Query {}; this.sequelizeStub = { log: stub(), options: {}, }; this.connectionStub = { uuid: 'test', }; }); it('logs before and after', function () { const debugStub = stub(); const qry = new this.cls(this.connectionStub, this.sequelizeStub, {}); const complete = qry._logQuery('SELECT 1', debugStub); complete(); expect(this.sequelizeStub.log).to.have.been.calledOnce; expect(this.sequelizeStub.log).to.have.been.calledWithMatch('Executing (test): SELECT 1'); expect(debugStub).to.have.been.calledWith('Executing (test): SELECT 1'); expect(debugStub).to.have.been.calledWith('Executed (test): SELECT 1'); }); it('logs before and after with benchmark', function () { const debugStub = stub(); const qry = new this.cls(this.connectionStub, this.sequelizeStub, { benchmark: true }); const complete = qry._logQuery('SELECT 1', debugStub); complete(); expect(this.sequelizeStub.log).to.have.been.calledOnce; expect(this.sequelizeStub.log).to.have.been.calledWithMatch( 'Executed (test): SELECT 1', match.number, { benchmark: true }, ); expect(debugStub).to.have.been.calledWith('Executing (test): SELECT 1'); expect(debugStub).to.have.been.calledWith('Executed (test): SELECT 1'); }); it('supports logging bigints', function () { // this test was added because while most of the time `bigint`s are stringified, // they're not always. For instance, if the PK is a bigint, calling .save() // will pass the PK as a bigint instead of a string as a parameter. // This is fine, as bigints should be supported natively, // but AbstractQuery#_logQuery used JSON.stringify on parameters, // which does not support serializing bigints (https://github.com/tc39/proposal-bigint/issues/24) // This test was added to ensure bigints don't cause a crash // when `logQueryParameters` is true. const sequelizeStub = { ...this.sequelizeStub, options: { ...this.sequelizeStub.options, logQueryParameters: true, }, }; const debugStub = stub(); const qry = new this.cls(this.connectionStub, sequelizeStub, {}); const complete = qry._logQuery('SELECT 1', debugStub, [1n]); complete(); expect(debugStub).to.have.been.calledWith( 'Executing (test): SELECT 1; with parameters [ 1n ]', ); expect(debugStub).to.have.been.calledWith( 'Executed (test): SELECT 1; with parameters [ 1n ]', ); }); }); });