2023-04-12 07:24:17 -07:00
import type { IDataObject , INode } from 'n8n-workflow' ;
2023-10-10 08:36:20 -07:00
import mysql2 from 'mysql2/promise' ;
2023-04-12 07:24:17 -07:00
import * as deleteTable from '../../v2/actions/database/deleteTable.operation' ;
2023-10-10 08:36:20 -07:00
2023-04-12 07:24:17 -07:00
import * as executeQuery from '../../v2/actions/database/executeQuery.operation' ;
import * as insert from '../../v2/actions/database/insert.operation' ;
import * as select from '../../v2/actions/database/select.operation' ;
import * as update from '../../v2/actions/database/update.operation' ;
import * as upsert from '../../v2/actions/database/upsert.operation' ;
import type { Mysql2Pool , QueryRunner } from '../../v2/helpers/interfaces' ;
import { configureQueryRunner } from '../../v2/helpers/utils' ;
2023-10-10 08:36:20 -07:00
import { createMockExecuteFunction } from '@test/nodes/Helpers' ;
2023-04-12 07:24:17 -07:00
const mySqlMockNode : INode = {
id : '1' ,
name : 'MySQL node' ,
typeVersion : 2 ,
type : 'n8n-nodes-base.mySql' ,
position : [ 60 , 760 ] ,
parameters : {
operation : 'select' ,
} ,
} ;
const fakeConnection = {
format ( query : string , values : any [ ] ) {
return mysql2 . format ( query , values ) ;
} ,
2023-04-24 04:17:08 -07:00
query : jest.fn ( async ( _query = '' ) = > [ { } ] ) ,
2023-04-12 07:24:17 -07:00
release : jest.fn ( ) ,
beginTransaction : jest.fn ( ) ,
commit : jest.fn ( ) ,
rollback : jest.fn ( ) ,
} ;
const createFakePool = ( connection : IDataObject ) = > {
return {
getConnection() {
return connection ;
} ,
2023-04-24 04:17:08 -07:00
query : jest.fn ( async ( ) = > [ { } ] ) ,
2023-04-12 07:24:17 -07:00
} as unknown as Mysql2Pool ;
} ;
const emptyInputItems = [ { json : { } , pairedItem : { item : 0 , input : undefined } } ] ;
describe ( 'Test MySql V2, operations' , ( ) = > {
afterEach ( ( ) = > {
jest . clearAllMocks ( ) ;
} ) ;
it ( 'should have all operations' , ( ) = > {
expect ( deleteTable . execute ) . toBeDefined ( ) ;
expect ( deleteTable . description ) . toBeDefined ( ) ;
expect ( executeQuery . execute ) . toBeDefined ( ) ;
expect ( executeQuery . description ) . toBeDefined ( ) ;
expect ( insert . execute ) . toBeDefined ( ) ;
expect ( insert . description ) . toBeDefined ( ) ;
expect ( select . execute ) . toBeDefined ( ) ;
expect ( select . description ) . toBeDefined ( ) ;
expect ( update . execute ) . toBeDefined ( ) ;
expect ( update . description ) . toBeDefined ( ) ;
expect ( upsert . execute ) . toBeDefined ( ) ;
expect ( upsert . description ) . toBeDefined ( ) ;
} ) ;
it ( 'deleteTable: drop, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'deleteTable' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
deleteCommand : 'drop' ,
options : { } ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const poolQuerySpy = jest . spyOn ( pool , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await deleteTable . execute . call ( fakeExecuteFunction , emptyInputItems , runQueries ) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : [ { item : 0 } ] } ] ) ;
2023-04-12 07:24:17 -07:00
expect ( poolQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( poolQuerySpy ) . toBeCalledWith ( 'DROP TABLE IF EXISTS `test_table`' ) ;
} ) ;
it ( 'deleteTable: truncate, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'deleteTable' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
deleteCommand : 'truncate' ,
options : { } ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const poolQuerySpy = jest . spyOn ( pool , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await deleteTable . execute . call ( fakeExecuteFunction , emptyInputItems , runQueries ) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : [ { item : 0 } ] } ] ) ;
2023-04-12 07:24:17 -07:00
expect ( poolQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( poolQuerySpy ) . toBeCalledWith ( 'TRUNCATE TABLE `test_table`' ) ;
} ) ;
it ( 'deleteTable: delete, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'deleteTable' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
deleteCommand : 'delete' ,
where : {
values : [
{
column : 'id' ,
condition : 'equal' ,
value : '1' ,
} ,
{
column : 'name' ,
condition : 'LIKE' ,
value : 'some%' ,
} ,
] ,
} ,
options : { } ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const poolQuerySpy = jest . spyOn ( pool , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await deleteTable . execute . call ( fakeExecuteFunction , emptyInputItems , runQueries ) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : [ { item : 0 } ] } ] ) ;
2023-04-12 07:24:17 -07:00
expect ( poolQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( poolQuerySpy ) . toBeCalledWith (
"DELETE FROM `test_table` WHERE `id` = '1' AND `name` LIKE 'some%'" ,
) ;
} ) ;
it ( 'executeQuery, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'executeQuery' ,
query :
"DROP TABLE IF EXISTS $1:name;\ncreate table $1:name (id INT, name TEXT);\ninsert into $1:name (id, name) values (1, 'test 1');\nselect * from $1:name;\n" ,
options : {
queryBatching : 'independently' ,
queryReplacement : 'test_table' ,
} ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const fakeConnectionCopy = { . . . fakeConnection } ;
fakeConnectionCopy . query = jest . fn ( async ( query? : string ) = > {
const result = [ ] ;
if ( query ? . toLowerCase ( ) . includes ( 'select' ) ) {
result . push ( [ { id : 1 , name : 'test 1' } ] ) ;
} else {
result . push ( { } ) ;
}
2023-04-24 04:17:08 -07:00
return result ;
2023-04-12 07:24:17 -07:00
} ) ;
const pool = createFakePool ( fakeConnectionCopy ) ;
const connectionQuerySpy = jest . spyOn ( fakeConnectionCopy , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await executeQuery . execute . call (
fakeExecuteFunction ,
emptyInputItems ,
runQueries ,
nodeOptions ,
) ;
expect ( result ) . toBeDefined ( ) ;
expect ( result ) . toEqual ( [
{
json : {
id : 1 ,
name : 'test 1' ,
} ,
pairedItem : {
item : 0 ,
} ,
} ,
] ) ;
expect ( connectionQuerySpy ) . toBeCalledTimes ( 4 ) ;
expect ( connectionQuerySpy ) . toBeCalledWith ( 'DROP TABLE IF EXISTS `test_table`' ) ;
expect ( connectionQuerySpy ) . toBeCalledWith ( 'create table `test_table` (id INT, name TEXT)' ) ;
expect ( connectionQuerySpy ) . toBeCalledWith (
"insert into `test_table` (id, name) values (1, 'test 1')" ,
) ;
expect ( connectionQuerySpy ) . toBeCalledWith ( 'select * from `test_table`' ) ;
} ) ;
2024-04-08 18:48:17 -07:00
it ( 'executeQuery, should parse numbers' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'executeQuery' ,
query : 'SELECT * FROM users LIMIT $1, $2' ,
options : {
queryBatching : 'independently' ,
queryReplacement : '2, 5' ,
nodeVersion : 2.3 ,
} ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const fakeConnectionCopy = { . . . fakeConnection } ;
fakeConnectionCopy . query = jest . fn ( async ( query? : string ) = > {
return [ { query } ] ;
} ) ;
const pool = createFakePool ( fakeConnectionCopy ) ;
const connectionQuerySpy = jest . spyOn ( fakeConnectionCopy , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await executeQuery . execute . call (
fakeExecuteFunction ,
emptyInputItems ,
runQueries ,
nodeOptions ,
) ;
expect ( result ) . toBeDefined ( ) ;
expect ( connectionQuerySpy ) . toBeCalledWith ( 'SELECT * FROM users LIMIT 2, 5' ) ;
} ) ;
2023-04-12 07:24:17 -07:00
it ( 'select, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'select' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
limit : 2 ,
where : {
values : [
{
column : 'id' ,
condition : '>' ,
value : '1' ,
} ,
{
column : 'name' ,
value : 'test' ,
} ,
] ,
} ,
combineConditions : 'OR' ,
sort : {
values : [
{
column : 'id' ,
direction : 'DESC' ,
} ,
] ,
} ,
options : {
queryBatching : 'transaction' ,
detailedOutput : false ,
} ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const connectionQuerySpy = jest . spyOn ( fakeConnection , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
2023-08-25 08:38:09 -07:00
{ . . . nodeOptions , nodeVersion : 2 } ,
2023-04-12 07:24:17 -07:00
pool ,
) ;
const result = await select . execute . call ( fakeExecuteFunction , emptyInputItems , runQueries ) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : { item : 0 } } ] ) ;
2023-04-12 07:24:17 -07:00
const connectionBeginTransactionSpy = jest . spyOn ( fakeConnection , 'beginTransaction' ) ;
const connectionCommitSpy = jest . spyOn ( fakeConnection , 'commit' ) ;
expect ( connectionBeginTransactionSpy ) . toBeCalledTimes ( 1 ) ;
expect ( connectionQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( connectionQuerySpy ) . toBeCalledWith (
"SELECT * FROM `test_table` WHERE `id` > 1 OR `name` undefined 'test' ORDER BY `id` DESC LIMIT 2" ,
) ;
expect ( connectionCommitSpy ) . toBeCalledTimes ( 1 ) ;
} ) ;
it ( 'insert, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
dataMode : 'defineBelow' ,
valuesToSend : {
values : [
{
column : 'id' ,
value : '2' ,
} ,
{
column : 'name' ,
value : 'name 2' ,
} ,
] ,
} ,
options : {
queryBatching : 'independently' ,
priority : 'HIGH_PRIORITY' ,
detailedOutput : false ,
skipOnConflict : true ,
} ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const connectionQuerySpy = jest . spyOn ( fakeConnection , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const result = await insert . execute . call (
fakeExecuteFunction ,
emptyInputItems ,
runQueries ,
nodeOptions ,
) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : { item : 0 } } ] ) ;
2023-04-12 07:24:17 -07:00
expect ( connectionQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( connectionQuerySpy ) . toBeCalledWith (
"INSERT HIGH_PRIORITY IGNORE INTO `test_table` (`id`, `name`) VALUES ('2','name 2')" ,
) ;
} ) ;
it ( 'update, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'update' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
dataMode : 'autoMapInputData' ,
columnToMatchOn : 'id' ,
options : {
queryBatching : 'independently' ,
} ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const connectionQuerySpy = jest . spyOn ( fakeConnection , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const inputItems = [
{
json : {
id : 42 ,
name : 'test 4' ,
} ,
} ,
{
json : {
id : 88 ,
name : 'test 88' ,
} ,
} ,
] ;
const result = await update . execute . call (
fakeExecuteFunction ,
inputItems ,
runQueries ,
nodeOptions ,
) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [
{ json : { success : true } , pairedItem : { item : 0 } } ,
{ json : { success : true } , pairedItem : { item : 1 } } ,
] ) ;
2023-04-12 07:24:17 -07:00
expect ( connectionQuerySpy ) . toBeCalledTimes ( 2 ) ;
expect ( connectionQuerySpy ) . toBeCalledWith (
"UPDATE `test_table` SET `name` = 'test 4' WHERE `id` = 42" ,
) ;
expect ( connectionQuerySpy ) . toBeCalledWith (
"UPDATE `test_table` SET `name` = 'test 88' WHERE `id` = 88" ,
) ;
} ) ;
it ( 'upsert, should call runQueries with' , async ( ) = > {
const nodeParameters : IDataObject = {
operation : 'upsert' ,
table : {
__rl : true ,
value : 'test_table' ,
mode : 'list' ,
cachedResultName : 'test_table' ,
} ,
columnToMatchOn : 'id' ,
dataMode : 'autoMapInputData' ,
options : { } ,
} ;
const nodeOptions = nodeParameters . options as IDataObject ;
const pool = createFakePool ( fakeConnection ) ;
const poolQuerySpy = jest . spyOn ( pool , 'query' ) ;
const fakeExecuteFunction = createMockExecuteFunction ( nodeParameters , mySqlMockNode ) ;
const runQueries : QueryRunner = configureQueryRunner . call (
fakeExecuteFunction ,
nodeOptions ,
pool ,
) ;
const inputItems = [
{
json : {
id : 42 ,
name : 'test 4' ,
} ,
} ,
{
json : {
id : 88 ,
name : 'test 88' ,
} ,
} ,
] ;
const result = await upsert . execute . call (
fakeExecuteFunction ,
inputItems ,
runQueries ,
nodeOptions ,
) ;
expect ( result ) . toBeDefined ( ) ;
2023-10-10 08:36:20 -07:00
expect ( result ) . toEqual ( [ { json : { success : true } , pairedItem : [ { item : 0 } , { item : 1 } ] } ] ) ;
2023-04-12 07:24:17 -07:00
expect ( poolQuerySpy ) . toBeCalledTimes ( 1 ) ;
expect ( poolQuerySpy ) . toBeCalledWith (
"INSERT INTO `test_table`(`id`, `name`) VALUES(42,'test 4') ON DUPLICATE KEY UPDATE `name` = 'test 4';INSERT INTO `test_table`(`id`, `name`) VALUES(88,'test 88') ON DUPLICATE KEY UPDATE `name` = 'test 88'" ,
) ;
} ) ;
} ) ;