2021-10-10 20:15:47 +03:00
/*eslint no-var:0, max-len:0 */
'use strict' ;
const chai = require ( 'chai' ) ;
chai . use ( require ( 'chai-as-promised' ) ) ;
2021-10-13 01:19:56 +03:00
chai . use ( require ( 'sinon-chai' ) ) ;
2021-10-10 20:15:47 +03:00
const expect = chai . expect ;
const Knex = require ( '../../../../knex' ) ;
const _ = require ( 'lodash' ) ;
const delay = require ( '../../../../lib/execution/internal/delay' ) ;
const {
isPostgreSQL ,
isOracle ,
isRedshift ,
isSQLite ,
isMysql ,
isMssql ,
isPgBased ,
isPgNative ,
isCockroachDB ,
} = require ( '../../../util/db-helpers' ) ;
const { DRIVER _NAMES : drivers } = require ( '../../../util/constants' ) ;
const {
dropTables ,
createAccounts ,
createTestTableTwo ,
} = require ( '../../../util/tableCreatorHelper' ) ;
const {
getAllDbs ,
getKnexForDb ,
} = require ( '../../util/knex-instance-provider' ) ;
const logger = require ( '../../../integration/logger' ) ;
const { insertAccounts } = require ( '../../../util/dataInsertHelper' ) ;
2021-10-13 01:19:56 +03:00
const sinon = require ( 'sinon' ) ;
2021-10-10 20:15:47 +03:00
describe ( 'Additional' , function ( ) {
getAllDbs ( ) . forEach ( ( db ) => {
describe ( db , ( ) => {
let knex ;
before ( async ( ) => {
knex = logger (
getKnexForDb ( db , {
pool : {
destroyTimeoutMillis : 1500 ,
} ,
} )
) ;
await dropTables ( knex ) ;
await createAccounts ( knex , false , false ) ;
await createTestTableTwo ( knex ) ;
} ) ;
beforeEach ( async ( ) => {
await knex ( 'accounts' ) . truncate ( ) ;
await insertAccounts ( knex ) ;
} ) ;
after ( async ( ) => {
await knex . destroy ( ) ;
} ) ;
describe ( 'Custom response processing' , ( ) => {
before ( 'setup custom response handler' , ( ) => {
knex . client . config . postProcessResponse = ( response , queryContext ) => {
response . callCount = response . callCount
? response . callCount + 1
: 1 ;
response . queryContext = queryContext ;
return response ;
} ;
} ) ;
after ( 'restore client configuration' , ( ) => {
knex . client . config . postProcessResponse = null ;
} ) ;
it ( 'should process normal response' , ( ) => {
return knex ( 'accounts' )
. limit ( 1 )
. then ( ( res ) => {
expect ( res . callCount ) . to . equal ( 1 ) ;
} ) ;
} ) ;
it ( 'should pass query context to the custom handler' , ( ) => {
return knex ( 'accounts' )
. queryContext ( 'the context' )
. limit ( 1 )
. then ( ( res ) => {
expect ( res . queryContext ) . to . equal ( 'the context' ) ;
} ) ;
} ) ;
it ( 'should process raw response' , ( ) => {
return knex . raw ( 'select * from ??' , [ 'accounts' ] ) . then ( ( res ) => {
expect ( res . callCount ) . to . equal ( 1 ) ;
} ) ;
} ) ;
it ( 'should pass query context for raw responses' , ( ) => {
return knex
. raw ( 'select * from ??' , [ 'accounts' ] )
. queryContext ( 'the context' )
. then ( ( res ) => {
expect ( res . queryContext ) . to . equal ( 'the context' ) ;
} ) ;
} ) ;
it ( 'should process response done in transaction' , ( ) => {
return knex
. transaction ( ( trx ) => {
return trx ( 'accounts' )
. limit ( 1 )
. then ( ( res ) => {
expect ( res . callCount ) . to . equal ( 1 ) ;
return res ;
} ) ;
} )
. then ( ( res ) => {
expect ( res . callCount ) . to . equal ( 1 ) ;
} ) ;
} ) ;
it ( 'should pass query context for responses from transactions' , ( ) => {
return knex
. transaction ( ( trx ) => {
return trx ( 'accounts' )
. queryContext ( 'the context' )
. limit ( 1 )
. then ( ( res ) => {
expect ( res . queryContext ) . to . equal ( 'the context' ) ;
return res ;
} ) ;
} )
. then ( ( res ) => {
expect ( res . queryContext ) . to . equal ( 'the context' ) ;
} ) ;
} ) ;
it ( 'should handle error correctly in a stream' , ( done ) => {
const stream = knex ( 'wrongtable' ) . limit ( 1 ) . stream ( ) ;
stream . on ( 'error' , ( ) => {
done ( ) ;
} ) ;
} ) ;
it ( 'should process response done through a stream' , ( done ) => {
let response ;
const stream = knex ( 'accounts' ) . limit ( 1 ) . stream ( ) ;
stream . on ( 'data' , ( res ) => {
response = res ;
} ) ;
stream . on ( 'finish' , ( ) => {
expect ( response . callCount ) . to . equal ( 1 ) ;
done ( ) ;
} ) ;
} ) ;
it ( 'should pass query context for responses through a stream' , ( done ) => {
let response ;
const stream = knex ( 'accounts' )
. queryContext ( 'the context' )
. limit ( 1 )
. stream ( ) ;
stream . on ( 'data' , ( res ) => {
response = res ;
} ) ;
stream . on ( 'finish' , ( ) => {
expect ( response . queryContext ) . to . equal ( 'the context' ) ;
done ( ) ;
} ) ;
} ) ;
it ( 'should process response for each row done through a stream' , ( done ) => {
const stream = knex ( 'accounts' ) . limit ( 5 ) . stream ( ) ;
let count = 0 ;
stream . on ( 'data' , ( ) => count ++ ) ;
stream . on ( 'finish' , ( ) => {
expect ( count ) . to . equal ( 5 ) ;
done ( ) ;
} ) ;
} ) ;
} ) ;
describe ( 'columnInfo with wrapIdentifier and postProcessResponse' , ( ) => {
before ( 'setup hooks' , ( ) => {
knex . client . config . postProcessResponse = ( response ) => {
return _ . mapKeys ( response , ( val , key ) => {
return _ . camelCase ( key ) ;
} ) ;
} ;
knex . client . config . wrapIdentifier = ( id , origImpl ) => {
return origImpl ( _ . snakeCase ( id ) ) ;
} ;
} ) ;
after ( 'restore client configuration' , ( ) => {
knex . client . config . postProcessResponse = null ;
knex . client . config . wrapIdentifier = null ;
} ) ;
it ( 'should work using camelCased table name' , ( ) => {
return knex ( 'testTableTwo' )
. columnInfo ( )
. then ( ( res ) => {
expect ( Object . keys ( res ) ) . to . have . all . members ( [
'id' ,
'accountId' ,
'details' ,
'status' ,
] ) ;
} ) ;
} ) ;
it ( 'should work using snake_cased table name' , ( ) => {
return knex ( 'test_table_two' )
. columnInfo ( )
. then ( ( res ) => {
expect ( Object . keys ( res ) ) . to . have . all . members ( [
'id' ,
'accountId' ,
'details' ,
'status' ,
] ) ;
} ) ;
} ) ;
} ) ;
describe ( 'returning with wrapIdentifier and postProcessResponse` (TODO: fix to work on all possible dialects)' , function ( ) {
const origHooks = { } ;
if ( ! isPostgreSQL ( knex ) || ! isMssql ( knex ) ) {
return ;
}
before ( 'setup custom hooks' , ( ) => {
origHooks . postProcessResponse =
knex . client . config . postProcessResponse ;
origHooks . wrapIdentifier = knex . client . config . wrapIdentifier ;
// Add `_foo` to each identifier.
knex . client . config . postProcessResponse = ( res ) => {
if ( Array . isArray ( res ) ) {
return res . map ( ( it ) => {
if ( typeof it === 'object' ) {
return _ . mapKeys ( it , ( value , key ) => {
return key + '_foo' ;
} ) ;
} else {
return it ;
}
} ) ;
} else {
return res ;
}
} ;
// Remove `_foo` from the end of each identifier.
knex . client . config . wrapIdentifier = ( id ) => {
return id . substring ( 0 , id . length - 4 ) ;
} ;
} ) ;
after ( 'restore hooks' , ( ) => {
knex . client . config . postProcessResponse =
origHooks . postProcessResponse ;
knex . client . config . wrapIdentifier = origHooks . wrapIdentifier ;
} ) ;
it ( 'should return the correct column when a single property is given to returning' , ( ) => {
return knex ( 'accounts_foo' )
. insert ( { balance _foo : 123 } )
. returning ( 'balance_foo' )
. then ( ( res ) => {
expect ( res ) . to . eql ( [ 123 ] ) ;
} ) ;
} ) ;
it ( 'should return the correct columns when multiple properties are given to returning' , ( ) => {
return knex ( 'accounts_foo' )
. insert ( { balance _foo : 123 , email _foo : 'foo@bar.com' } )
. returning ( [ 'balance_foo' , 'email_foo' ] )
. then ( ( res ) => {
expect ( res ) . to . eql ( [
{ balance _foo : 123 , email _foo : 'foo@bar.com' } ,
] ) ;
} ) ;
} ) ;
} ) ;
2021-10-11 19:36:50 +03:00
describe ( 'other operations' , ( ) => {
it ( 'should truncate a table with truncate' , function ( ) {
return knex ( 'test_table_two' )
. truncate ( )
. testSql ( function ( tester ) {
tester ( 'mysql' , 'truncate `test_table_two`' ) ;
tester ( 'pg' , 'truncate "test_table_two" restart identity' ) ;
tester ( 'pgnative' , 'truncate "test_table_two" restart identity' ) ;
tester ( 'pg-redshift' , 'truncate "test_table_two"' ) ;
tester ( 'sqlite3' , 'delete from `test_table_two`' ) ;
tester ( 'oracledb' , 'truncate table "test_table_two"' ) ;
tester ( 'mssql' , 'truncate table [test_table_two]' ) ;
} )
. then ( ( ) => {
return knex ( 'test_table_two' )
. select ( '*' )
. then ( ( resp ) => {
expect ( resp ) . to . have . length ( 0 ) ;
} ) ;
} )
. then ( ( ) => {
// Insert new data after truncate and make sure ids restart at 1.
// This doesn't currently work on oracle, where the created sequence
// needs to be manually reset.
// On redshift, one would need to create an entirely new table and do
// `insert into ... (select ...); alter table rename...`
if ( isOracle ( knex ) || isRedshift ( knex ) ) {
return ;
}
return knex ( 'test_table_two' )
. insert ( { status : 1 } )
. then ( ( res ) => {
return knex ( 'test_table_two' )
. select ( 'id' )
. first ( )
. then ( ( res ) => {
expect ( res ) . to . be . an ( 'object' ) ;
if ( ! isCockroachDB ( knex ) ) {
expect ( res . id ) . to . equal ( 1 ) ;
}
} ) ;
} ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'should allow raw queries directly with `knex.raw`' , function ( ) {
const tables = {
[ drivers . MySQL ] : 'SHOW TABLES' ,
[ drivers . MySQL2 ] : 'SHOW TABLES' ,
[ drivers . CockroachDB ] :
"SELECT table_name FROM information_schema.tables WHERE table_schema='public'" ,
[ drivers . PostgreSQL ] :
"SELECT table_name FROM information_schema.tables WHERE table_schema='public'" ,
[ drivers . PgNative ] :
"SELECT table_name FROM information_schema.tables WHERE table_schema='public'" ,
[ drivers . Redshift ] :
"SELECT table_name FROM information_schema.tables WHERE table_schema='public'" ,
[ drivers . SQLite ] :
"SELECT name FROM sqlite_master WHERE type='table';" ,
[ drivers . Oracle ] : 'select TABLE_NAME from USER_TABLES' ,
[ drivers . MsSQL ] :
"SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='dbo'" ,
} ;
return knex
. raw ( tables [ knex . client . driverName ] )
. testSql ( function ( tester ) {
tester ( knex . client . driverName , tables [ knex . client . driverName ] ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'should allow using the primary table as a raw statement' , function ( ) {
expect ( knex ( knex . raw ( 'raw_table_name' ) ) . toQuery ( ) ) . to . equal (
'select * from raw_table_name'
) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'should allow using .fn-methods to create raw statements' , function ( ) {
expect ( knex . fn . now ( ) . prototype === knex . raw ( ) . prototype ) ;
expect ( knex . fn . now ( ) . toQuery ( ) ) . to . equal ( 'CURRENT_TIMESTAMP' ) ;
expect ( knex . fn . now ( 6 ) . toQuery ( ) ) . to . equal ( 'CURRENT_TIMESTAMP(6)' ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( '#2184 - should properly escape table name for SQLite columnInfo' , function ( ) {
if ( ! isSQLite ( knex ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
return knex . schema
. dropTableIfExists ( 'group' )
. then ( function ( ) {
return knex . schema . createTable ( 'group' , function ( table ) {
table . integer ( 'foo' ) ;
} ) ;
} )
. then ( function ( ) {
return knex ( 'group' ) . columnInfo ( ) ;
} )
. then ( function ( columnInfo ) {
expect ( columnInfo ) . to . deep . equal ( {
foo : {
type : 'integer' ,
maxLength : null ,
nullable : true ,
defaultValue : null ,
} ,
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if ( isOracle ( knex ) ) {
const oracledb = require ( 'oracledb' ) ;
describe ( 'test oracle stored procedures' , function ( ) {
it ( 'create stored procedure' , function ( ) {
return knex
. raw (
`
2021-10-10 20:15:47 +03:00
CREATE OR REPLACE PROCEDURE SYSTEM . multiply ( X IN NUMBER , Y IN NUMBER , OUTPUT OUT NUMBER )
IS
BEGIN
OUTPUT : = X * Y ;
END ; `
2021-10-11 19:36:50 +03:00
)
. then ( function ( result ) {
expect ( result ) . to . be . an ( 'array' ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'get outbound values from stored procedure' , function ( ) {
const bindVars = {
x : 6 ,
y : 7 ,
output : {
dir : oracledb . BIND _OUT ,
} ,
} ;
return knex
. raw ( 'BEGIN SYSTEM.MULTIPLY(:x, :y, :output); END;' , bindVars )
. then ( function ( result ) {
expect ( result [ 0 ] ) . to . be . ok ;
expect ( result [ 0 ] ) . to . equal ( '42' ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'drop stored procedure' , function ( ) {
const bindVars = { x : 6 , y : 7 } ;
return knex
. raw ( 'drop procedure SYSTEM.MULTIPLY' , bindVars )
. then ( function ( result ) {
expect ( result ) . to . be . ok ;
expect ( result ) . to . be . an ( 'array' ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
}
2021-10-11 19:36:50 +03:00
it ( 'should allow renaming a column' , function ( ) {
let countColumn ;
if ( isOracle ( knex ) ) {
countColumn = 'COUNT(*)' ;
} else if ( isMssql ( knex ) ) {
countColumn = '' ;
} else {
countColumn = 'count(*)' ;
}
let count ;
const inserts = [ ] ;
_ . times ( 40 , function ( i ) {
inserts . push ( {
email : 'email' + i ,
first _name : 'Test' ,
last _name : 'Data' ,
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
return knex ( 'accounts' )
. insert ( inserts )
. then ( function ( ) {
return knex . count ( '*' ) . from ( 'accounts' ) ;
} )
. then ( function ( resp ) {
count = resp [ 0 ] [ countColumn ] ;
return knex . schema
. table ( 'accounts' , function ( t ) {
t . renameColumn ( 'about' , 'about_col' ) ;
} )
. testSql ( function ( tester ) {
tester ( 'mysql' , [
'show fields from `accounts` where field = ?' ,
] ) ;
tester ( 'pg' , [
'alter table "accounts" rename "about" to "about_col"' ,
] ) ;
tester ( 'pgnative' , [
'alter table "accounts" rename "about" to "about_col"' ,
] ) ;
tester ( 'pg-redshift' , [
'alter table "accounts" rename "about" to "about_col"' ,
] ) ;
tester ( 'sqlite3' , [
'alter table `accounts` rename `about` to `about_col`' ,
] ) ;
tester ( 'oracledb' , [
'DECLARE PK_NAME VARCHAR(200); IS_AUTOINC NUMBER := 0; BEGIN EXECUTE IMMEDIATE (\'ALTER TABLE "accounts" RENAME COLUMN "about" TO "about_col"\'); SELECT COUNT(*) INTO IS_AUTOINC from "USER_TRIGGERS" where trigger_name = \'accounts_autoinc_trg\'; IF (IS_AUTOINC > 0) THEN SELECT cols.column_name INTO PK_NAME FROM all_constraints cons, all_cons_columns cols WHERE cons.constraint_type = \'P\' AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner AND cols.table_name = \'accounts\'; IF (\'about_col\' = PK_NAME) THEN EXECUTE IMMEDIATE (\'DROP TRIGGER "accounts_autoinc_trg"\'); EXECUTE IMMEDIATE (\'create or replace trigger "accounts_autoinc_trg" BEFORE INSERT on "accounts" for each row declare checking number := 1; begin if (:new."about_col" is null) then while checking >= 1 loop select "accounts_seq".nextval into :new."about_col" from dual; select count("about_col") into checking from "accounts" where "about_col" = :new."about_col"; end loop; end if; end;\'); end if; end if;END;' ,
] ) ;
tester ( 'mssql' , [ "exec sp_rename ?, ?, 'COLUMN'" ] ) ;
} ) ;
} )
. then ( function ( ) {
return knex . count ( '*' ) . from ( 'accounts' ) ;
} )
. then ( function ( resp ) {
expect ( resp [ 0 ] [ countColumn ] ) . to . equal ( count ) ;
} )
. then ( function ( ) {
return knex ( 'accounts' ) . select ( 'about_col' ) ;
} )
. then ( function ( ) {
return knex . schema . table ( 'accounts' , function ( t ) {
t . renameColumn ( 'about_col' , 'about' ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} )
. then ( function ( ) {
return knex . count ( '*' ) . from ( 'accounts' ) ;
} )
. then ( function ( resp ) {
expect ( resp [ 0 ] [ countColumn ] ) . to . equal ( count ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'should allow dropping a column' , async function ( ) {
let countColumn ;
if ( isOracle ( knex ) ) {
countColumn = 'COUNT(*)' ;
} else if ( isMssql ( knex ) ) {
countColumn = '' ;
} else {
countColumn = 'count(*)' ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
let count ;
await knex
. count ( '*' )
. from ( 'accounts' )
. then ( function ( resp ) {
count = resp [ 0 ] [ countColumn ] ;
} )
. then ( function ( ) {
return knex . schema
. table ( 'accounts' , function ( t ) {
t . dropColumn ( 'first_name' ) ;
} )
. testSql ( function ( tester ) {
tester ( 'mysql' , [ 'alter table `accounts` drop `first_name`' ] ) ;
tester ( 'pg' , [
'alter table "accounts" drop column "first_name"' ,
] ) ;
tester ( 'pgnative' , [
'alter table "accounts" drop column "first_name"' ,
] ) ;
tester ( 'pg-redshift' , [
'alter table "accounts" drop column "first_name"' ,
] ) ;
tester ( 'sqlite3' , [ 'PRAGMA table_info(`accounts`)' ] ) ;
tester ( 'oracledb' , [
'alter table "accounts" drop ("first_name")' ,
] ) ;
//tester('oracledb', ['alter table "accounts" drop ("first_name")']);
tester ( 'mssql' , [
"\n DECLARE @constraint varchar(100) = (SELECT default_constraints.name\n FROM sys.all_columns\n INNER JOIN sys.tables\n ON all_columns.object_id = tables.object_id\n INNER JOIN sys.schemas\n ON tables.schema_id = schemas.schema_id\n INNER JOIN sys.default_constraints\n ON all_columns.default_object_id = default_constraints.object_id\n WHERE schemas.name = 'dbo'\n AND tables.name = 'accounts'\n AND all_columns.name = 'first_name')\n\n IF @constraint IS NOT NULL EXEC('ALTER TABLE accounts DROP CONSTRAINT ' + @constraint)" ,
'ALTER TABLE [accounts] DROP COLUMN [first_name]' ,
] ) ;
} ) ;
} )
. then ( function ( ) {
return knex . select ( '*' ) . from ( 'accounts' ) . first ( ) ;
} )
. then ( function ( resp ) {
expect ( _ . keys ( resp ) . sort ( ) ) . to . eql ( [
'about' ,
'balance' ,
'created_at' ,
'email' ,
'id' ,
'last_name' ,
'logins' ,
'phone' ,
'updated_at' ,
] ) ;
} )
. then ( function ( ) {
return knex . count ( '*' ) . from ( 'accounts' ) ;
} )
. then ( function ( resp ) {
expect ( resp [ 0 ] [ countColumn ] ) . to . equal ( count ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
await dropTables ( knex ) ;
await createAccounts ( knex , false , false ) ;
await createTestTableTwo ( knex ) ;
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
describe ( 'timeouts' , ( ) => {
it ( '.timeout() should throw TimeoutError' , async function ( ) {
const driverName = knex . client . driverName ;
if ( isSQLite ( knex ) ) {
return this . skip ( ) ;
} //TODO -- No built-in support for sleeps
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if ( isRedshift ( knex ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const testQueries = {
[ drivers . CockroachDB ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(1)' ) ;
} ,
[ drivers . PostgreSQL ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(1)' ) ;
} ,
[ drivers . PgNative ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(1)' ) ;
} ,
[ drivers . MySQL ] : function ( ) {
return knex . raw ( 'SELECT SLEEP(1)' ) ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return knex . raw ( 'SELECT SLEEP(1)' ) ;
} ,
[ drivers . MsSQL ] : function ( ) {
return knex . raw ( "WAITFOR DELAY '00:00:01'" ) ;
} ,
[ drivers . Oracle ] : function ( ) {
return knex . raw ( 'begin dbms_lock.sleep(1); end;' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if ( ! Object . prototype . hasOwnProperty . call ( testQueries , driverName ) ) {
throw new Error ( 'Missing test query for driver: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const query = testQueries [ driverName ] ( ) ;
await query
. timeout ( 200 )
. then ( function ( ) {
expect ( true ) . to . equal ( false ) ;
} )
. catch ( function ( error ) {
expect ( _ . pick ( error , 'timeout' , 'name' , 'message' ) ) . to . deep . equal (
{
timeout : 200 ,
name : 'KnexTimeoutError' ,
message :
'Defined query timeout of 200ms exceeded when running query.' ,
}
) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( '.timeout(ms, {cancel: true}) should throw TimeoutError and cancel slow query' , async function ( ) {
if ( isSQLite ( knex ) || isCockroachDB ( knex ) ) {
return this . skip ( ) ;
} //TODO -- No built-in support for sleeps
if ( isRedshift ( knex ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
// There's unexpected behavior caused by knex releasing a connection back
// to the pool because of a timeout when a long query is still running.
// A subsequent query will acquire the connection (still in-use) and hang
// until the first query finishes. Setting a sleep time longer than the
// mocha timeout exposes this behavior.
const testQueries = {
[ drivers . PostgreSQL ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . CockroachDB ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . PgNative ] : function ( ) {
return knex . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . MySQL ] : function ( ) {
return knex . raw ( 'SELECT SLEEP(10)' ) ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return knex . raw ( 'SELECT SLEEP(10)' ) ;
} ,
[ drivers . MsSQL ] : function ( ) {
return knex . raw ( "WAITFOR DELAY '00:00:10'" ) ;
} ,
[ drivers . Oracle ] : function ( ) {
return knex . raw ( 'begin dbms_lock.sleep(10); end;' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const driverName = knex . client . driverName ;
if ( ! Object . prototype . hasOwnProperty . call ( testQueries , driverName ) ) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const query = testQueries [ driverName ] ( ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
function addTimeout ( ) {
return query . timeout ( 200 , { cancel : true } ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
// Only mysql/postgres query cancelling supported for now
if ( ! isMysql ( knex ) && ! isPgBased ( knex ) ) {
expect ( addTimeout ) . to . throw (
'Query cancelling not supported for this dialect'
) ;
return ; // TODO: Use `this.skip()` here?
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const getProcessesQueries = {
[ drivers . CockroachDB ] : function ( ) {
return knex . raw ( 'SELECT * FROM [SHOW CLUSTER STATEMENTS]' ) ;
} ,
[ drivers . PostgreSQL ] : function ( ) {
return knex . raw ( 'SELECT * from pg_stat_activity' ) ;
} ,
[ drivers . PgNative ] : function ( ) {
return knex . raw ( 'SELECT * from pg_stat_activity' ) ;
} ,
[ drivers . MySQL ] : function ( ) {
return knex . raw ( 'SHOW PROCESSLIST' ) ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return knex . raw ( 'SHOW PROCESSLIST' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if (
! Object . prototype . hasOwnProperty . call (
getProcessesQueries ,
driverName
)
) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const getProcessesQuery = getProcessesQueries [ driverName ] ( ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
try {
await addTimeout ( ) ;
expect ( true ) . to . equal ( false ) ;
} catch ( error ) {
expect ( _ . pick ( error , 'timeout' , 'name' , 'message' ) ) . to . deep . equal ( {
timeout : 200 ,
name : 'KnexTimeoutError' ,
message :
'Defined query timeout of 200ms exceeded when running query.' ,
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
// Ensure sleep command is removed.
// This query will hang if a connection gets released back to the pool
// too early.
// 50ms delay since killing query doesn't seem to have immediate effect to the process listing
await delay ( 50 ) ;
const results = await getProcessesQuery ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
let processes ;
let sleepProcess ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if ( isPgBased ( knex ) ) {
processes = results . rows ;
sleepProcess = _ . find ( processes , { query : query . toString ( ) } ) ;
} else {
processes = results [ 0 ] ;
sleepProcess = _ . find ( processes , {
Info : 'SELECT SLEEP(10)' ,
} ) ;
}
expect ( sleepProcess ) . to . equal ( undefined ) ;
}
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( '.timeout(ms, {cancel: true}) should throw TimeoutError and cancel slow query in transaction' , async function ( ) {
if ( isSQLite ( knex ) || isCockroachDB ( knex ) ) {
return this . skip ( ) ;
} //TODO -- No built-in support for sleeps
if ( isRedshift ( knex ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
// There's unexpected behavior caused by knex releasing a connection back
// to the pool because of a timeout when a long query is still running.
// A subsequent query will acquire the connection (still in-use) and hang
// until the first query finishes. Setting a sleep time longer than the
// mocha timeout exposes this behavior.
const testQueries = {
[ drivers . CockroachDB ] : function ( ) {
return 'SELECT pg_sleep(10)' ;
} ,
[ drivers . PostgreSQL ] : function ( ) {
return 'SELECT pg_sleep(10)' ;
} ,
[ drivers . PgNative ] : function ( ) {
return 'SELECT pg_sleep(10)' ;
} ,
[ drivers . MySQL ] : function ( ) {
return 'SELECT SLEEP(10)' ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return 'SELECT SLEEP(10)' ;
} ,
[ drivers . MsSQL ] : function ( ) {
return "WAITFOR DELAY '00:00:10'" ;
} ,
[ drivers . Oracle ] : function ( ) {
return 'begin dbms_lock.sleep(10); end;' ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const driverName = knex . client . driverName ;
if ( ! Object . prototype . hasOwnProperty . call ( testQueries , driverName ) ) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const query = testQueries [ driverName ] ( ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
function addTimeout ( ) {
return knex . raw ( query ) . timeout ( 200 , { cancel : true } ) ;
2021-10-10 20:15:47 +03:00
}
2021-10-11 19:36:50 +03:00
// Only mysql/postgres query cancelling supported for now
if ( ! isMysql ( knex ) && ! isPgBased ( knex ) ) {
expect ( addTimeout ) . to . throw (
'Query cancelling not supported for this dialect'
) ;
return ; // TODO: Use `this.skip()` here?
2021-10-10 20:15:47 +03:00
}
2021-10-11 19:36:50 +03:00
const getProcessesQueries = {
[ drivers . CockroachDB ] : function ( ) {
return knex . raw ( 'SELECT * FROM [SHOW CLUSTER STATEMENTS]' ) ;
} ,
[ drivers . PostgreSQL ] : function ( ) {
return knex . raw ( 'SELECT * from pg_stat_activity' ) ;
} ,
[ drivers . PgNative ] : function ( ) {
return knex . raw ( 'SELECT * from pg_stat_activity' ) ;
} ,
[ drivers . MySQL ] : function ( ) {
return knex . raw ( 'SHOW PROCESSLIST' ) ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return knex . raw ( 'SHOW PROCESSLIST' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if (
! Object . prototype . hasOwnProperty . call (
getProcessesQueries ,
driverName
)
) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
2021-10-10 20:15:47 +03:00
}
2021-10-11 19:36:50 +03:00
const getProcessesQuery = getProcessesQueries [ driverName ] ( ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
await knex
. transaction ( ( trx ) => addTimeout ( ) . transacting ( trx ) )
. then ( function ( ) {
expect ( true ) . to . equal ( false ) ;
} )
. catch ( async function ( error ) {
expect ( _ . pick ( error , 'timeout' , 'name' , 'message' ) ) . to . deep . equal (
{
timeout : 200 ,
name : 'KnexTimeoutError' ,
message :
'Defined query timeout of 200ms exceeded when running query.' ,
}
) ;
// Ensure sleep command is removed.
// This query will hang if a connection gets released back to the pool
// too early.
// 50ms delay since killing query doesn't seem to have immediate effect to the process listing
await delay ( 50 ) ;
const results = await getProcessesQuery ;
let processes ;
let sleepProcess ;
if ( _ . startsWith ( driverName , 'pg' ) ) {
processes = results . rows ;
sleepProcess = _ . find ( processes , { query : query . toString ( ) } ) ;
} else {
processes = results [ 0 ] ;
sleepProcess = _ . find ( processes , {
Info : 'SELECT SLEEP(10)' ,
} ) ;
}
expect ( sleepProcess ) . to . equal ( undefined ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( '.timeout(ms, {cancel: true}) should cancel slow query even if connection pool is exhausted' , async function ( ) {
// Only mysql/postgres query cancelling supported for now
if ( isCockroachDB ( knex ) || ( ! isMysql ( knex ) && ! isPgBased ( knex ) ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
// To make this test easier, I'm changing the pool settings to max 1.
// Also setting acquireTimeoutMillis to lower as not to wait the default time
const knexConfig = _ . cloneDeep ( knex . client . config ) ;
knexConfig . pool . min = 0 ;
knexConfig . pool . max = 1 ;
knexConfig . pool . acquireTimeoutMillis = 100 ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const knexDb = new Knex ( knexConfig ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const testQueries = {
[ drivers . CockroachDB ] : function ( ) {
return knexDb . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . PostgreSQL ] : function ( ) {
return knexDb . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . PgNative ] : function ( ) {
return knexDb . raw ( 'SELECT pg_sleep(10)' ) ;
} ,
[ drivers . MySQL ] : function ( ) {
return knexDb . raw ( 'SELECT SLEEP(10)' ) ;
} ,
[ drivers . MySQL2 ] : function ( ) {
return knexDb . raw ( 'SELECT SLEEP(10)' ) ;
} ,
[ drivers . MsSQL ] : function ( ) {
return knexDb . raw ( "WAITFOR DELAY '00:00:10'" ) ;
} ,
[ drivers . Oracle ] : function ( ) {
return knexDb . raw ( 'begin dbms_lock.sleep(10); end;' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const driverName = knex . client . driverName ;
if ( ! Object . prototype . hasOwnProperty . call ( testQueries , driverName ) ) {
throw new Error ( 'Missing test query for dialect: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const query = testQueries [ driverName ] ( ) ;
// We must use the original knex instance without the exhausted pool to list running queries
const getProcessesForDriver = {
[ drivers . CockroachDB ] : async ( ) => {
const results = await knex . raw (
'SELECT * FROM [SHOW CLUSTER STATEMENTS]'
) ;
return _ . map (
_ . filter ( results . rows , { state : 'active' } ) ,
'query'
) ;
} ,
[ drivers . PostgreSQL ] : async ( ) => {
const results = await knex . raw ( 'SELECT * from pg_stat_activity' ) ;
return _ . map (
_ . filter ( results . rows , { state : 'active' } ) ,
'query'
) ;
} ,
[ drivers . PgNative ] : async ( ) => {
const results = await knex . raw ( 'SELECT * from pg_stat_activity' ) ;
return _ . map (
_ . filter ( results . rows , { state : 'active' } ) ,
'query'
) ;
} ,
[ drivers . MySQL ] : async ( ) => {
const results = await knex . raw ( 'SHOW PROCESSLIST' ) ;
return _ . map ( results [ 0 ] , 'Info' ) ;
} ,
[ drivers . MySQL2 ] : async ( ) => {
const results = await knex . raw ( 'SHOW PROCESSLIST' ) ;
return _ . map ( results [ 0 ] , 'Info' ) ;
} ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
if (
! Object . prototype . hasOwnProperty . call (
getProcessesForDriver ,
driverName
)
) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const getProcesses = getProcessesForDriver [ driverName ] ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
try {
const promise = query
. timeout ( 50 , { cancel : true } )
. then ( _ . identity ) ;
await delay ( 15 ) ;
const processesBeforeTimeout = await getProcesses ( ) ;
expect ( processesBeforeTimeout ) . to . include ( query . toString ( ) ) ;
await expect ( promise ) . to . eventually . be . rejected . and . to . deep . include (
{
timeout : 50 ,
name : 'KnexTimeoutError' ,
message :
'Defined query timeout of 50ms exceeded when running query.' ,
}
) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const processesAfterTimeout = await getProcesses ( ) ;
expect ( processesAfterTimeout ) . to . not . include ( query . toString ( ) ) ;
} finally {
await knexDb . destroy ( ) ;
}
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( '.timeout(ms, {cancel: true}) should release connections after failing if connection cancellation throws an error' , async function ( ) {
// Only mysql/postgres query cancelling supported for now
if ( isCockroachDB ( knex ) || ( ! isPgBased ( knex ) && ! isMysql ( knex ) ) ) {
return this . skip ( ) ;
}
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
// To make this test easier, I'm changing the pool settings to max 1.
// Also setting acquireTimeoutMillis to lower as not to wait the default time
const knexConfig = _ . cloneDeep ( knex . client . config ) ;
knexConfig . pool . min = 0 ;
knexConfig . pool . max = 2 ;
knexConfig . pool . acquireTimeoutMillis = 100 ;
const rawTestQueries = {
[ drivers . CockroachDB ] : ( sleepSeconds ) =>
` SELECT pg_sleep( ${ sleepSeconds } ) ` ,
[ drivers . PostgreSQL ] : ( sleepSeconds ) =>
` SELECT pg_sleep( ${ sleepSeconds } ) ` ,
[ drivers . PgNative ] : ( sleepSeconds ) =>
` SELECT pg_sleep( ${ sleepSeconds } ) ` ,
[ drivers . MySQL ] : ( sleepSeconds ) => ` SELECT SLEEP( ${ sleepSeconds } ) ` ,
[ drivers . MySQL2 ] : ( sleepSeconds ) => ` SELECT SLEEP( ${ sleepSeconds } ) ` ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const driverName = knex . client . driverName ;
if (
! Object . prototype . hasOwnProperty . call ( rawTestQueries , driverName )
) {
throw new Error ( 'Missing test query for driverName: ' + driverName ) ;
2021-10-10 20:15:47 +03:00
}
2021-10-11 19:36:50 +03:00
const knexDb = new Knex ( knexConfig ) ;
const getTestQuery = ( sleepSeconds = 10 ) => {
const rawTestQuery = rawTestQueries [ driverName ] ( sleepSeconds ) ;
return knexDb . raw ( rawTestQuery ) ;
} ;
const knexPrototype = Object . getPrototypeOf ( knexDb . client ) ;
const originalWrappedCancelQueryCall =
knexPrototype . _wrappedCancelQueryCall ;
knexPrototype . _wrappedCancelQueryCall = ( conn , connectionToKill ) => {
if ( isPgNative ( knex ) || isCockroachDB ( knex ) || isMysql ( knex ) ) {
throw new Error ( 'END THIS' ) ;
} else {
return knexPrototype . query ( conn , {
method : 'raw' ,
sql : 'TestError' ,
} ) ;
}
} ;
const queryTimeout = 10 ;
const secondQueryTimeout = 11 ;
2021-10-10 20:15:47 +03:00
try {
2021-10-11 19:36:50 +03:00
await expect (
getTestQuery ( ) . timeout ( queryTimeout , { cancel : true } )
) . to . be . eventually . rejected . and . deep . include ( {
timeout : queryTimeout ,
name :
isCockroachDB ( knex ) || isMysql ( knex ) || isPgNative ( knex )
? 'Error'
: 'error' ,
message : ` After query timeout of ${ queryTimeout } ms exceeded, cancelling of query failed. ` ,
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
knexPrototype . _wrappedCancelQueryCall =
originalWrappedCancelQueryCall ;
try {
await getTestQuery ( ) . timeout ( secondQueryTimeout , {
cancel : true ,
} ) ;
} catch ( err ) {
console . log ( err ) ;
}
2021-10-10 20:15:47 +03:00
await expect (
getTestQuery ( ) . timeout ( secondQueryTimeout , { cancel : true } )
) . to . be . eventually . rejected . and . deep . include ( {
timeout : secondQueryTimeout ,
name : 'KnexTimeoutError' ,
message : ` Defined query timeout of ${ secondQueryTimeout } ms exceeded when running query. ` ,
} ) ;
2021-10-11 19:36:50 +03:00
} finally {
await knexDb . destroy ( ) ;
2021-10-10 20:15:47 +03:00
}
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
describe ( 'Events' , ( ) => {
it ( 'Event: query-response' , function ( ) {
let queryCount = 0 ;
const onQueryResponse = function ( response , obj , builder ) {
queryCount ++ ;
expect ( response ) . to . be . an ( 'array' ) ;
expect ( obj ) . to . be . an ( 'object' ) ;
expect ( obj . _ _knexUid ) . to . be . a ( 'string' ) ;
expect ( obj . _ _knexQueryUid ) . to . be . a ( 'string' ) ;
expect ( builder ) . to . be . an ( 'object' ) ;
} ;
knex . on ( 'query-response' , onQueryResponse ) ;
return knex ( 'accounts' )
. select ( )
. on ( 'query-response' , onQueryResponse )
. then ( function ( ) {
return knex . transaction ( function ( tr ) {
return tr ( 'accounts' )
. select ( )
. on ( 'query-response' , onQueryResponse ) ; //Transactions should emit the event as well
} ) ;
} )
. then ( function ( ) {
knex . removeListener ( 'query-response' , onQueryResponse ) ;
expect ( queryCount ) . to . equal ( 4 ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
it ( 'Event: preserves listeners on a copy with user params' , function ( ) {
let queryCount = 0 ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const onQueryResponse = function ( response , obj , builder ) {
queryCount ++ ;
expect ( response ) . to . be . an ( 'array' ) ;
expect ( obj ) . to . be . an ( 'object' ) ;
expect ( obj . _ _knexUid ) . to . be . a ( 'string' ) ;
expect ( obj . _ _knexQueryUid ) . to . be . a ( 'string' ) ;
expect ( builder ) . to . be . an ( 'object' ) ;
} ;
knex . on ( 'query-response' , onQueryResponse ) ;
const knexCopy = knex . withUserParams ( { } ) ;
return knexCopy ( 'accounts' )
. select ( )
. on ( 'query-response' , onQueryResponse )
. then ( function ( ) {
return knexCopy . transaction ( function ( tr ) {
return tr ( 'accounts' )
. select ( )
. on ( 'query-response' , onQueryResponse ) ; //Transactions should emit the event as well
} ) ;
} )
. then ( function ( ) {
expect ( Object . keys ( knex . _events ) . length ) . to . equal ( 1 ) ;
expect ( Object . keys ( knexCopy . _events ) . length ) . to . equal ( 1 ) ;
knex . removeListener ( 'query-response' , onQueryResponse ) ;
expect ( Object . keys ( knex . _events ) . length ) . to . equal ( 0 ) ;
expect ( queryCount ) . to . equal ( 4 ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'Event: query-error' , function ( ) {
let queryCountKnex = 0 ;
let queryCountBuilder = 0 ;
const onQueryErrorKnex = function ( error , obj ) {
queryCountKnex ++ ;
expect ( obj ) . to . be . an ( 'object' ) ;
expect ( obj . _ _knexUid ) . to . be . a ( 'string' ) ;
expect ( obj . _ _knexQueryUid ) . to . be . a ( 'string' ) ;
expect ( error ) . to . be . an ( 'error' ) ;
} ;
const onQueryErrorBuilder = function ( error , obj ) {
queryCountBuilder ++ ;
expect ( obj ) . to . be . an ( 'object' ) ;
expect ( obj . _ _knexUid ) . to . be . a ( 'string' ) ;
expect ( obj . _ _knexQueryUid ) . to . be . a ( 'string' ) ;
expect ( error ) . to . be . an ( 'error' ) ;
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
knex . on ( 'query-error' , onQueryErrorKnex ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
return knex
. raw ( 'Broken query' )
. on ( 'query-error' , onQueryErrorBuilder )
. then ( function ( ) {
expect ( true ) . to . equal ( false ) ; //Should not be resolved
} )
. catch ( function ( ) {
knex . removeListener ( 'query-error' , onQueryErrorKnex ) ;
knex . removeListener ( 'query-error' , onQueryErrorBuilder ) ;
expect ( queryCountBuilder ) . to . equal ( 1 ) ;
expect ( queryCountKnex ) . to . equal ( 1 ) ;
2021-10-10 20:15:47 +03:00
} ) ;
2021-10-11 19:36:50 +03:00
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( 'Event: start' , function ( ) {
return knex ( 'accounts' )
. insert ( { last _name : 'Start event test' } )
. then ( function ( ) {
const queryBuilder = knex ( 'accounts' ) . select ( ) ;
queryBuilder . on ( 'start' , function ( builder ) {
//Alter builder prior to compilation
//Select only one row
builder . where ( 'last_name' , 'Start event test' ) . first ( ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
return queryBuilder ;
} )
. then ( function ( row ) {
expect ( row ) . to . exist ;
expect ( row . last _name ) . to . equal ( 'Start event test' ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
it ( "Event 'query' should not emit native sql string" , function ( ) {
const builder = knex ( 'accounts' ) . where ( 'id' , 1 ) . select ( ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
builder . on ( 'query' , function ( obj ) {
const native = builder . toSQL ( ) . toNative ( ) . sql ;
const sql = builder . toSQL ( ) . sql ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
//Only assert if they diff to begin with.
//IE Maria does not diff
if ( native !== sql ) {
expect ( obj . sql ) . to . not . equal ( builder . toSQL ( ) . toNative ( ) . sql ) ;
expect ( obj . sql ) . to . equal ( builder . toSQL ( ) . sql ) ;
}
} ) ;
return builder ;
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
describe ( 'async stack traces' , function ( ) {
before ( ( ) => {
knex . client . config . asyncStackTraces = true ;
} ) ;
after ( ( ) => {
delete knex . client . config . asyncStackTraces ;
} ) ;
it ( 'should capture stack trace on raw query' , ( ) => {
return knex
. raw ( 'select * from some_nonexisten_table' )
. catch ( ( err ) => {
expect ( err . stack . split ( '\n' ) [ 2 ] ) . to . match ( /at Object\.raw \(/ ) ; // the index 2 might need adjustment if the code is refactored
expect ( typeof err . originalStack ) . to . equal ( 'string' ) ;
} ) ;
} ) ;
2021-10-11 19:36:50 +03:00
2021-10-10 20:15:47 +03:00
it ( 'should capture stack trace on schema builder' , ( ) => {
return knex . schema
. renameTable ( 'some_nonexisten_table' , 'whatever' )
. catch ( ( err ) => {
expect ( err . stack . split ( '\n' ) [ 1 ] ) . to . match (
/client\.schemaBuilder/
) ; // the index 1 might need adjustment if the code is refactored
expect ( typeof err . originalStack ) . to . equal ( 'string' ) ;
} ) ;
} ) ;
} ) ;
2021-10-13 01:19:56 +03:00
describe ( 'misc' , ( ) => {
2021-10-11 19:36:50 +03:00
it ( 'Overwrite knex.logger functions using config' , async ( ) => {
const knexConfig = {
... knex . client . config ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
let callCount = 0 ;
const assertCall = function ( expectedMessage , message ) {
expect ( message ) . to . equal ( expectedMessage ) ;
callCount ++ ;
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
knexConfig . log = {
warn : assertCall . bind ( null , 'test' ) ,
error : assertCall . bind ( null , 'test' ) ,
debug : assertCall . bind ( null , 'test' ) ,
deprecate : assertCall . bind (
null ,
'test is deprecated, please use test2'
) ,
} ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
//Sqlite warning message
knexConfig . useNullAsDefault = true ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
const knexDb = new Knex ( knexConfig ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
knexDb . client . logger . warn ( 'test' ) ;
knexDb . client . logger . error ( 'test' ) ;
knexDb . client . logger . debug ( 'test' ) ;
knexDb . client . logger . deprecate ( 'test' , 'test2' ) ;
2021-10-10 20:15:47 +03:00
2021-10-11 19:36:50 +03:00
expect ( callCount ) . to . equal ( 4 ) ;
await knexDb . destroy ( ) ;
} ) ;
2021-10-13 01:19:56 +03:00
it ( 'should allow destroying the pool with knex.destroy' , async function ( ) {
const knexConfig = {
... knex . client . config ,
} ;
const knexDb = new Knex ( knexConfig ) ;
const spy = sinon . spy ( knexDb . client . pool , 'destroy' ) ;
await knexDb . destroy ( ) ;
expect ( spy ) . to . have . callCount ( 1 ) ;
expect ( knexDb . client . pool ) . to . equal ( undefined ) ;
await knexDb . destroy ( ) ;
expect ( spy ) . to . have . callCount ( 1 ) ;
spy . restore ( ) ;
} ) ;
it ( 'should allow initialize the pool with knex.initialize' , async function ( ) {
const knexConfig = {
... knex . client . config ,
} ;
const knexDb = new Knex ( knexConfig ) ;
await knexDb . destroy ( ) ;
expect ( knexDb . client . pool ) . to . equal ( undefined ) ;
knexDb . initialize ( ) ;
expect ( knexDb . client . pool . destroyed ) . to . equal ( false ) ;
const waitForDestroy = knexDb . destroy ( ) ;
expect ( knexDb . client . pool . destroyed ) . to . equal ( true ) ;
return waitForDestroy . then ( ( ) => {
expect ( knexDb . client . pool ) . to . equal ( undefined ) ;
knexDb . initialize ( ) ;
expect ( knexDb . client . pool . destroyed ) . to . equal ( false ) ;
} ) ;
} ) ;
2021-10-10 20:15:47 +03:00
} ) ;
} ) ;
} ) ;
} ) ;