/* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. */ /****************************************************************************** * * You may not use the identified files except in compliance with the Apache * License, Version 2.0 (the "License.") * * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * * The node-oracledb test suite uses 'mocha', 'should' and 'async'. * See LICENSE.md for relevant licenses. * * NAME * 2. pool.js * * DESCRIPTION * Testing properties of connection pool. * *****************************************************************************/ 'use strict'; var oracledb = require('oracledb'); var async = require('async'); var should = require('should'); var dbConfig = require('./dbconfig.js'); describe('2. pool.js', function() { describe('2.1 default settting', function() { it('2.1.1 testing default values of pool properties', function(done) { oracledb.createPool( { user: dbConfig.user, password: dbConfig.password, connectString: dbConfig.connectString }, function(err, pool) { should.not.exist(err); pool.should.be.ok(); var defaultMin = 0; var defaultMax = 4; var defaultIncrement = 1; var defaultTimeout = 60; var defaultStmtCacheSize = 30; pool.poolMin.should.be.exactly(defaultMin).and.be.a.Number(); pool.poolMax.should.be.exactly(defaultMax).and.be.a.Number(); pool.poolIncrement.should.be.exactly(defaultIncrement).and.be.a.Number(); pool.poolTimeout.should.be.exactly(defaultTimeout).and.be.a.Number(); pool.stmtCacheSize.should.be.exactly(defaultStmtCacheSize).and.be.a.Number(); pool.connectionsOpen.should.equal(0); pool.connectionsInUse.should.equal(0); pool.terminate(function(err){ should.not.exist(err); done(); }); } ); }); }); describe('2.2 poolMin', function() { it('2.2.1 poolMin cannot be a negative number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : -5, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); should.not.exist(pool); done(); } ); }); it('2.2.2 poolMin must be a Number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : NaN, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); should.not.exist(pool); done(); } ); }); it('2.2.3 poolMin cannot greater than poolMax', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 10, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('ORA-24413:'); should.not.exist(pool); done(); } ); }); it('2.2.4 (poolMin + poolIncrement) can equal to poolMax', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 4, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.not.exist(err); pool.should.be.ok(); should.strictEqual(pool.connectionsInUse, 0); pool.terminate(function(err){ should.not.exist(err); done(); }); } ); }); }); // 2.2 describe('2.3 poolMax', function(){ it('2.3.1 poolMax cannot be a negative value', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 5, poolMax : -5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); should.not.exist(pool); done(); } ); }); it('2.3.2 poolMax cannot be 0', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 0, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('ORA-24413:'); should.not.exist(pool); done(); } ); }); it('2.3.3 poolMax must be a number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : true, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); should.not.exist(pool); done(); } ); }); it('2.3.4 poolMax and poolMin actually limit the pool size', function (done) { var conns=[], pool, isPoolClosed=false; async.series([ function (cb) { oracledb.createPool({ ...dbConfig, poolMax: 2, poolMin: 1, poolTimeout: 1, queueTimeout: 1, }, function (err, newPool) { pool = newPool; cb(err); }); }, function (cb) { pool.getConnection(function (err, newConn) { if (!newConn) { cb(new Error("Failed to create connection")); return; } conns.push(newConn); cb(err); }); }, function (cb) { pool.getConnection(function (err, newConn) { if (!newConn) { cb(new Error("Failed to create connection")); return; } conns.push(newConn); cb(err); }); }, // The third connection should throw NJS-040 error within queueTimeout ms, // No NJS-040 error means poolMax parameter failed to limit connection number function (cb) { pool.getConnection(function (err) { if (err && err.message.startsWith("NJS-040")) { cb(); } else { cb(new Error("PoolMax failed to limit connection numbers")); } }); }, function (cb) { conns[0].close(function (err) { cb(err); }); }, function (cb) { conns[1].close(function (err) { cb(err); }); }, function (cb) { setTimeout(function() { // The number of remaining connections after poolTimeout seconds should >= poolMin. // The number of remaining connections < poolMin means poolMin parameter failed to limit connection number if (pool.connectionsOpen < 1) { cb(new Error("PoolMin failed to limit connection numbers")); return; } pool.close(function (err) { if (!err) isPoolClosed=true; cb(err); }); }, 2000); }, ], function (err) { if (!isPoolClosed) { pool.close(0, function (err) { done(err); }); } else { done(err); } }); }); }); // 2.3 describe('2.4 poolIncrement', function(){ it('2.4.1 poolIncrement cannot be a negative value', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : -1, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); // NJS-007: invalid value for should.not.exist(pool); done(); } ); }); it('2.4.2 poolIncrement must be a Number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 10, poolIncrement : false, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); should.not.exist(pool); done(); } ); }); it('2.4.3 the amount of open connections equals to poolMax when (connectionsOpen + poolIncrement) > poolMax', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 4, poolIncrement : 2, poolTimeout : 28, stmtCacheSize : 23 }, function(err, pool){ should.not.exist(err); pool.should.be.ok(); pool.getConnection( function(err, conn1){ should.not.exist(err); conn1.should.be.ok(); pool.connectionsOpen.should.be.exactly(1); pool.connectionsInUse.should.be.exactly(1); pool.getConnection( function(err, conn2){ should.not.exist(err); conn2.should.be.ok(); pool.connectionsInUse.should.be.exactly(2); pool.getConnection( function(err, conn3){ should.not.exist(err); conn3.should.be.ok(); pool.connectionsOpen.should.be.exactly(3); pool.connectionsInUse.should.be.exactly(3); // (connectionsOpen + poolIncrement) > poolMax pool.getConnection( function(err, conn4){ should.not.exist(err); conn4.should.be.ok(); pool.connectionsOpen.should.be.exactly(4); pool.connectionsOpen.should.be.exactly(4); conn4.release( function(err){ should.not.exist(err); conn3.release( function(err){ should.not.exist(err); conn2.release( function(err){ should.not.exist(err); conn1.release( function(err){ should.not.exist(err); pool.terminate( function(err){ should.not.exist(err); done(); }); }); }); }); }); }); }); }); }); } ); }); }); // 2.4 describe('2.5 poolTimeout', function() { it('2.5.1 poolTimeout cannot be a negative number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : -5, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); // NJS-007: invalid value for should.not.exist(pool); done(); } ); }); it('2.5.2 poolTimeout can be 0, which disables timeout feature', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : 0, stmtCacheSize : 23 }, function(err, pool){ should.not.exist(err); pool.should.be.ok(); pool.terminate(function(err){ should.not.exist(err); done(); }); } ); }); it('2.5.3 poolTimeout must be a number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : NaN, stmtCacheSize : 23 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); // NJS-007: invalid value for should.not.exist(pool); done(); } ); }); }); describe('2.6 stmtCacheSize', function() { it('2.6.1 stmtCacheSize cannot be a negative value', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : -9 }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); // NJS-007: invalid value for should.not.exist(pool); done(); } ); }); it('2.6.2 stmtCacheSize can be 0', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : 0 }, function(err, pool){ should.not.exist(err); pool.should.be.ok(); pool.terminate(function(err){ should.not.exist(err); done(); }); } ); }); it('2.6.3 stmtCacheSize must be a Number', function(done){ oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 5, poolIncrement : 1, poolTimeout : 28, stmtCacheSize : NaN }, function(err, pool){ should.exist(err); (err.message).should.startWith('NJS-007:'); // NJS-007: invalid value for should.not.exist(pool); done(); } ); }); }); describe('2.7 getConnection', function(){ var pool1; beforeEach('get pool ready', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 1, poolMax : 2, poolIncrement : 1, poolTimeout : 1 }, function(err, pool){ should.not.exist(err); pool1 = pool; done(); } ); }); it('2.7.1 passes error in callback if called after pool is terminated and a callback is provided', function(done) { pool1.terminate(function(err) { should.not.exist(err); pool1.getConnection(function(err, conn) { should.exist(err); (err.message).should.startWith('NJS-065:'); // NJS-065: pool closed should.not.exist(conn); done(); }); }); }); }); describe('2.8 connection request queue', function(){ function getBlockingSql(secondsToBlock) { var blockingSql = '' + 'declare \n' + ' \n' + ' l_start timestamp with local time zone := systimestamp; \n' + ' \n' + 'begin \n' + ' \n' + ' loop \n' + ' exit when l_start + interval \'' + (secondsToBlock || 3) + '\' second <= systimestamp; \n' + ' end loop; \n' + ' \n' + 'end;'; return blockingSql; } it('2.8.1 basic case', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 1, poolIncrement : 1, poolTimeout : 1 }, function(err, pool){ should.not.exist(err); async.parallel( [ function(cb) { pool.getConnection(function(err, conn) { should.not.exist(err); conn.execute(getBlockingSql(3), function(err) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); cb(); }); }); }); }, function(cb) { //using setTimeout to help ensure this connection requests reaches DB later setTimeout(function() { pool.getConnection(function(err, conn) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); cb(); }); }); }, 100); } ], function(err){ should.not.exist(err); pool.terminate(function(err) { should.not.exist(err); done(); }); } ); } ); }); it('2.8.2 generates NJS-040 if request is queued and queueTimeout expires', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 1, poolIncrement : 1, poolTimeout : 1, queueTimeout : 2000 //2 seconds }, function(err, pool){ should.not.exist(err); async.parallel( [ function(cb) { pool.getConnection(function(err, conn) { should.not.exist(err); conn.execute(getBlockingSql(4), function(err) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); cb(); }); }); }); }, function(cb) { //using setTimeout to help ensure this gets to the db last setTimeout(function() { pool.getConnection(function(err, conn) { should.exist(err); (err.message.startsWith("NJS-040:")).should.be.true(); should.not.exist(conn); cb(); }); }, 100); } ], function(err){ should.not.exist(err); pool.terminate(function(err) { should.not.exist(err); done(); }); } ); } ); }); it('2.8.3 does not generate NJS-040 if request is queued for less time than queueTimeout', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 1, poolIncrement : 1, poolTimeout : 1, queueTimeout : 10000 //10 seconds }, function(err, pool){ should.not.exist(err); async.parallel( [ function(cb) { pool.getConnection(function(err, conn) { should.not.exist(err); conn.execute(getBlockingSql(4), function(err) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); cb(); }); }); }); }, function(cb) { //using setTimeout to help ensure this gets to the db last setTimeout(function() { pool.getConnection(function(err, conn) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); cb(); }); }); }, 100); } ], function(err){ should.not.exist(err); pool.terminate(function(err) { should.not.exist(err); done(); }); } ); } ); }); }); describe('2.9 _enableStats & _logStats functionality', function(){ it('2.9.1 does not work after the pool has been terminated', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 1, poolIncrement : 1, poolTimeout : 1, _enableStats : true }, function(err, pool){ should.not.exist(err); pool.getConnection(function(err, conn) { should.not.exist(err); conn.execute('select 1 from dual', function(err) { should.not.exist(err); conn.release(function(err) { should.not.exist(err); pool.terminate(function(err) { should.not.exist(err); try { pool._logStats(); } catch (err) { should.exist(err); (err.message).should.startWith("NJS-065:"); // NJS-065: pool closed } done(); }); }); }); }); } ); }); }); describe('2.10 Close method', function(){ it('2.10.1 close can be used as an alternative to release', function(done) { oracledb.createPool( { user : dbConfig.user, password : dbConfig.password, connectString : dbConfig.connectString, poolMin : 0, poolMax : 1, poolIncrement : 1, poolTimeout : 1 }, function(err, pool){ should.not.exist(err); pool.close(function(err) { should.not.exist(err); done(); }); } ); }); }); // 2.10 describe('2.11 Invalid Credential', function() { it('2.11.1 error occurs at creating pool when poolMin >= 1', function(done) { oracledb.createPool( { user: 'notexist', password: 'nopass', connectString: dbConfig.connectString, poolMin: 5 }, function(err, pool) { should.exist(err); (err.message).should.startWith('ORA-24413: '); // ORA-24413: Invalid number of sessions specified should.not.exist(pool); done(); } ); }); // 2.11.1 it('2.11.2 error occurs at getConnection() when poolMin is the default value 0', function(done) { oracledb.createPool( { user: 'notexist', password: 'nopass', connectString: dbConfig.connectString }, function(err, pool) { should.exist(pool); pool.getConnection(function(err, conn) { should.exist(err); (err.message).should.startWith('ORA-01017: '); // ORA-01017: invalid username/password; logon denied should.not.exist(conn); pool.close(function(err) { should.not.exist(err); done(); }); }); } ); }); // 2.11.2 }); // 2.11 describe('2.12 connectionString alias', function() { it('2.12.1 allows connectionString to be used as an alias for connectString', function(done) { oracledb.createPool( { user: dbConfig.user, password: dbConfig.password, connectionString: dbConfig.connectString, poolMin: 1, poolMax: 1, poolIncrement: 0 }, function(err, pool) { should.not.exist(err); pool.should.be.ok(); pool.close(function(err) { should.not.exist(err); done(); }); } ); }); }); // 2.12 });