Add support for dropForeign in SQLite (#4092)

Co-authored-by: Igor Savin <iselwin@gmail.com>
This commit is contained in:
Rijk van Zanten 2020-12-08 07:49:41 -05:00 committed by GitHub
parent c47e00de64
commit b7e1ffd7d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 489 additions and 258 deletions

View File

@ -29,12 +29,15 @@ class Oracledb_Compiler extends Oracle_Compiler {
insertValues.length === 1 &&
isEmpty(insertValues[0])
) {
const returningFragment = this.single.returning
? ' (' + this.formatter.wrap(this.single.returning) + ')'
: '';
return this._addReturningToSqlAndConvert(
'insert into ' +
this.tableName +
' (' +
this.formatter.wrap(this.single.returning) +
') values (default)',
returningFragment +
' values (default)',
outBinding[0],
this.tableName,
returning

View File

@ -309,6 +309,56 @@ assign(SQLite3_DDL.prototype, {
);
},
dropForeign: async function (columns, indexName) {
return this.client.transaction(
async (trx) => {
this.trx = trx;
const sql = await this.getTableSql();
const createTable = sql[0];
const oneLineSql = createTable.sql.replace(/\s+/g, ' ');
const matched = oneLineSql.match(/^CREATE TABLE\s+(\S+)\s*\((.*)\)/);
const defs = matched[2];
if (!defs) {
throw new Error('No column definitions in this statement!');
}
const updatedDefs = defs
.split(',')
.map((line) => line.trim())
.filter((defLine) => {
if (
defLine.startsWith('constraint') === false &&
defLine.includes('foreign key') === false
)
return true;
if (indexName) {
if (defLine.includes(indexName)) return false;
return true;
} else {
const matched = defLine.match(/\(`(\S+)`\)/);
const columnName = matched[1];
return columns.includes(columnName) === false;
}
})
.join(', ');
const newSql = oneLineSql.replace(defs, updatedDefs);
return this.reinsertMapped(createTable, newSql, (row) => {
return row;
});
},
{ connection: this.connection }
);
},
reinsertMapped(createTable, newSql, mapRow) {
return Promise.resolve()
.then(() => this.createTempTable(createTable))

View File

@ -50,6 +50,20 @@ TableCompiler_SQLite3.prototype.dropUnique = function (columns, indexName) {
this.pushQuery(`drop index ${indexName}`);
};
// Compile a drop foreign key command.
TableCompiler_SQLite3.prototype.dropForeign = function (columns, indexName) {
const compiler = this;
this.pushQuery({
sql: `PRAGMA table_info(${this.tableName()})`,
output(pragma) {
return compiler.client
.ddl(compiler, pragma, this.connection)
.dropForeign(columns, indexName);
},
});
};
TableCompiler_SQLite3.prototype.dropIndex = function (columns, indexName) {
indexName = indexName
? this.formatter.wrap(indexName)

View File

@ -17,13 +17,16 @@
"lint:everything": "npm run lint:types && npm run lint",
"test:unit": "mocha --exit -t 10000 test/db-less-test-suite.js && npm run test:tape && npm run test:cli",
"test:db": "mocha --exit -t 10000 test/integration-test-suite.js",
"test:db:no-oracle": "cross-env DB=\"mssql mysql mysql2 postgres sqlite3\" mocha --exit -t 10000 test/integration-test-suite.js",
"test": "mocha --exit -t 10000 test/all-tests-suite.js && npm run test:tape && npm run test:cli",
"test:coverage": "nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 test/all-tests-suite.js && npm run test:tape && npm run test:cli",
"test:everything": "npm run lint:everything && npm run test:coverage",
"test:sqlite": "cross-env DB=sqlite3 npm test",
"test:sqlite": "cross-env DB=sqlite3 npm run test:db",
"test:postgres": "cross-env DB=postgres npm run test:db",
"test:tape": "node test/tape/index.js | tap-spec",
"test:cli": "cross-env KNEX_PATH=../knex.js KNEX=bin/cli.js jake -f test/jake/Jakefile",
"db:start": "docker-compose -f scripts/docker-compose.yml up --build -d mysql oracledbxe postgres mssql; docker-compose -f scripts/docker-compose.yml up initmssqlknexdb waitmysql waitpostgres waitoracledbxe",
"db:start:no-oracle": "docker-compose -f scripts/docker-compose.yml up --build -d mysql postgres mssql && docker-compose -f scripts/docker-compose.yml up initmssqlknexdb waitmysql waitpostgres",
"db:stop": "docker-compose -f scripts/docker-compose.yml down",
"db:start:postgres": "docker-compose -f scripts/docker-compose.yml up --build -d postgres && docker-compose -f scripts/docker-compose.yml up waitpostgres",
"db:stop:postgres": "docker-compose -f scripts/docker-compose.yml down",

View File

@ -12,6 +12,8 @@ describe('Query Building Tests', function () {
this.timeout(process.env.KNEX_TEST_TIMEOUT || 5000);
require('./unit/query/builder');
require('./unit/query/formatter');
require('./unit/query/string');
require('./unit/schema/mysql')('mysql');
require('./unit/schema/mysql')('mysql2');
require('./unit/schema/postgres');

View File

@ -452,7 +452,7 @@ module.exports = function (knex) {
.catch(expectQueryEventToHaveBeenTriggered);
});
it('#1040, #1171 - When pool is filled with transaction connections, Non-transaction queries should not hang the application, but instead throw a timeout error', function () {
it('#1040, #1171 - When pool is filled with transaction connections, Non-transaction queries should not hang the application, but instead throw a timeout error', async () => {
//To make this test easier, I'm changing the pool settings to max 1.
const knexConfig = _.clone(knex.client.config);
knexConfig.pool.min = 0;
@ -463,7 +463,7 @@ module.exports = function (knex) {
//Create a transaction that will occupy the only available connection, and avoid trx.commit.
return knexDb.transaction(function (trx) {
await knexDb.transaction(function (trx) {
let sql = 'SELECT 1';
if (knex.client.driverName === 'oracledb') {
sql = 'SELECT 1 FROM DUAL';
@ -494,6 +494,8 @@ module.exports = function (knex) {
trx.commit(); //Test done
});
});
await knexDb.destroy();
});
it('#1694, #1703 it should return connections to pool if acquireConnectionTimeout is triggered', async function () {
@ -509,6 +511,8 @@ module.exports = function (knex) {
await expect(
db.transaction(() => db.transaction(() => ({})))
).to.be.rejectedWith(KnexTimeoutError);
await db.destroy();
});
/**

View File

@ -15,7 +15,7 @@ const _ = require('lodash');
const testMemoryMigrations = require('./memory-migrations');
module.exports = function (knex) {
require('rimraf').sync(path.join(__dirname, './migration'));
rimraf.sync(path.join(__dirname, './migration'));
before(function () {
// make sure lock was not left from previous failed test run
@ -84,11 +84,12 @@ module.exports = function (knex) {
'test/integration/migrate/rename-and-drop-column-with-multiline-sql-from-legacy-db',
},
});
const db = logger(knexLib(knexConfig));
const knexInstance = knexLib(knexConfig);
const db = logger(knexInstance);
await db.migrate.latest();
await db.migrate.rollback();
await knexInstance.destroy();
});
}
@ -189,8 +190,8 @@ module.exports = function (knex) {
});
});
it('should return a positive number if the DB is ahead', function () {
return Promise.all([
it('should return a positive number if the DB is ahead', async () => {
const [migration1, migration2, migration3] = await Promise.all([
knex('knex_migrations').returning('id').insert({
name: 'foobar',
batch: 5,
@ -206,26 +207,25 @@ module.exports = function (knex) {
batch: 6,
migration_time: new Date(),
}),
]).then(([migration1, migration2, migration3]) => {
return knex.migrate
.status({ directory: 'test/integration/migrate/test' })
.then(function (migrationLevel) {
expect(migrationLevel).to.equal(3);
})
.then(function () {
// Cleanup the added migrations
if (/redshift/.test(knex.client.driverName)) {
return knex('knex_migrations')
.where('name', 'like', '%foobar%')
.del();
}
]);
return knex.migrate
.status({ directory: 'test/integration/migrate/test' })
.then(function (migrationLevel) {
expect(migrationLevel).to.equal(3);
})
.then(function () {
// Cleanup the added migrations
if (/redshift/.test(knex.client.driverName)) {
return knex('knex_migrations')
.where('id', migration1[0])
.orWhere('id', migration2[0])
.orWhere('id', migration3[0])
.where('name', 'like', '%foobar%')
.del();
});
});
}
return knex('knex_migrations')
.where('id', migration1[0])
.orWhere('id', migration2[0])
.orWhere('id', migration3[0])
.del();
});
});
});
@ -991,12 +991,13 @@ module.exports = function (knex) {
const migrations = {
migration1: {
up(knex) {
return knex.schema.createTable('migration_source_test_1', function (
t
) {
t.increments();
t.string('name');
});
return knex.schema.createTable(
'migration_source_test_1',
function (t) {
t.increments();
t.string('name');
}
);
},
down(knex) {
return knex.schema.dropTable('migration_source_test_1');

View File

@ -0,0 +1,71 @@
const { expect } = require('chai');
module.exports = (knex) => {
describe('Schema', () => {
beforeEach(async () => {
await knex.schema
.createTable('foreign_keys_table_two', (table) => {
table.increments();
})
.createTable('foreign_keys_table_three', (table) => {
table.increments();
})
.createTable('foreign_keys_table_one', (table) => {
table.increments();
table.integer('fkey_two').unsigned().notNull();
table.foreign('fkey_two').references('foreign_keys_table_two.id');
table.integer('fkey_three').unsigned().notNull();
table
.foreign('fkey_three')
.references('foreign_keys_table_three.id')
.withKeyName('fk_fkey_threeee');
});
});
afterEach(async () => {
await knex.schema
.dropTable('foreign_keys_table_one')
.dropTable('foreign_keys_table_two')
.dropTable('foreign_keys_table_three');
});
describe('drop foreign key', () => {
it('correctly drops foreign key', async () => {
await knex('foreign_keys_table_two').insert({});
await knex('foreign_keys_table_three').insert({});
await knex('foreign_keys_table_one').insert({
fkey_two: 1,
fkey_three: 1,
});
try {
await knex('foreign_keys_table_one').insert({
fkey_two: 9999,
fkey_three: 99,
});
throw new Error("Shouldn't reach this");
} catch (err) {
if (knex.client.driverName === 'sqlite3') {
expect(err.message).to.equal(
`insert into \`foreign_keys_table_one\` (\`fkey_three\`, \`fkey_two\`) values (99, 9999) - SQLITE_CONSTRAINT: FOREIGN KEY constraint failed`
);
}
if (knex.client.driverName === 'postgres') {
expect(err.message).to.equal(
`insert into "foreign_keys_table_one" ("fkey_three", "fkey_two") values ($1, $2) - insert or update on table "foreign_keys_table_one" violates foreign key constraint "foreign_keys_table_one_fkey_two_foreign"`
);
}
expect(err.message).to.include('constraint');
}
await knex.schema.alterTable('foreign_keys_table_one', (table) => {
table.dropForeign(['fkey_two']);
table.dropForeign([], 'fk_fkey_threeee');
});
await knex('foreign_keys_table_one').insert({
fkey_two: 9999,
fkey_three: 99,
});
});
});
});
};

View File

@ -160,23 +160,25 @@ module.exports = (knex) => {
return Promise.resolve();
}
before(() =>
Promise.all([
knex.schema.createTable('increments_columns_1_test', (table) => {
before(async () => {
await knex.schema.createTable(
'increments_columns_1_test',
(table) => {
table.increments().comment('comment_1');
}),
knex.schema.createTable('increments_columns_2_test', (table) => {
}
);
await knex.schema.createTable(
'increments_columns_2_test',
(table) => {
table.increments('named_2').comment('comment_2');
}),
])
);
}
);
});
after(() =>
Promise.all([
knex.schema.dropTable('increments_columns_1_test'),
knex.schema.dropTable('increments_columns_2_test'),
])
);
after(async () => {
await knex.schema.dropTable('increments_columns_1_test');
await knex.schema.dropTable('increments_columns_2_test');
});
it('#2210 - creates an incrementing column with a comment', () => {
const table_name = 'increments_columns_1_test';
@ -1154,8 +1156,8 @@ module.exports = (knex) => {
describe('renameColumn', () => {
describe('without mappers', () => {
before(() =>
knex.schema
before(async () => {
await knex.schema
.createTable('rename_column_test', (tbl) => {
tbl.increments('id_test').unsigned().primary();
tbl
@ -1175,31 +1177,26 @@ module.exports = (knex) => {
.createTable('rename_col_test', (tbl) => {
tbl.integer('colnameint').defaultTo(1);
tbl.string('colnamestring').defaultTo('knex').notNullable();
})
.then(() => {
// without data, the column isn't found??
return knex
.insert({ parent_id_test: 1 })
.into('rename_column_test');
})
);
});
});
after(() =>
knex.schema
after(async () => {
await knex.schema
.dropTable('rename_column_foreign_test')
.dropTable('rename_column_test')
.dropTable('rename_col_test')
);
.dropTable('rename_col_test');
});
it('renames the column', () =>
knex.schema
.table('rename_column_test', (tbl) =>
tbl.renameColumn('id_test', 'id')
)
.then(() => knex.schema.hasColumn('rename_column_test', 'id'))
.then((exists) => {
expect(exists).to.equal(true);
}));
it('renames the column', async () => {
await knex.schema.table('rename_column_test', (tbl) =>
tbl.renameColumn('id_test', 'id')
);
const exists = await knex.schema.hasColumn(
'rename_column_test',
'id'
);
expect(exists).to.equal(true);
});
it('successfully renames a column referenced in a foreign key', () =>
knex.schema.table('rename_column_test', (tbl) => {

View File

@ -14,6 +14,7 @@ module.exports = function (knex) {
});
require('./schema')(knex);
require('./schema/foreign-keys')(knex);
require('./migrate/migration-integration-tests')(knex);
require('./seed')(knex);

View File

@ -25,7 +25,7 @@ const poolSqlite = {
acquireTimeoutMillis: 1000,
afterCreate: function (connection, callback) {
assert.ok(typeof connection.__knexUid !== 'undefined');
callback(null, connection);
connection.run('PRAGMA foreign_keys = ON', callback);
},
};

View File

@ -1,48 +1,203 @@
'use strict';
const _ = require('lodash');
const expect = require('chai').expect;
const knex = require('../../../knex');
const config = require('../../knexfile');
const sinon = require('sinon');
describe('OracleDb externalAuth', function () {
const knexInstance = knex({
client: 'oracledb',
connection: {
user: 'user',
password: 'password',
connectString: 'connect-string',
externalAuth: true,
host: 'host',
database: 'database',
},
});
let spy;
const knex = require('../../../knex');
const config = require('../../knexfile');
before(function () {
spy = sinon.spy(knexInstance.client.driver, 'getConnection');
describe('Oracle', () => {
describe('Compiler', () => {
const knexInstance = knex({
client: 'oracledb',
connection: {
user: 'user',
password: 'password',
connectString: 'connect-string',
externalAuth: true,
host: 'host',
database: 'database',
},
});
it('correctly builds single value insert', async () => {
const qb = knexInstance.client.queryBuilder();
qb.insert({ value: 1 }).into('fakeTable');
const compiler = knexInstance.client.queryCompiler(qb);
const sql = compiler.insert();
expect(sql).to.eql({
sql: 'insert into "fakeTable" ("value") values (?)',
outBinding: [[]],
returning: [],
});
});
it('correctly builds default values only insert', async () => {
const qb = knexInstance.client.queryBuilder();
qb.insert({}).into('fakeTable');
const compiler = knexInstance.client.queryCompiler(qb);
const sql = compiler.insert();
expect(sql).to.eql({
sql: 'insert into "fakeTable" values (default)',
outBinding: [[]],
returning: [],
});
});
it('correctly builds default values only insert and returning', async () => {
const qb = knexInstance.client.queryBuilder();
qb.insert({}).into('fakeTable').returning('id');
const compiler = knexInstance.client.queryCompiler(qb);
const sql = compiler.insert();
expect(sql).to.eql({
sql:
'insert into "fakeTable" ("id") values (default) returning "id" into ?',
outBinding: [['id']],
returning: ['id'],
});
});
});
it('externalAuth and connectString should be sent to the getConnection', function () {
const connectionWithExternalAuth = {
connectString: 'connect-string',
externalAuth: true,
};
knexInstance.client.acquireRawConnection().then(
function (resolve) {},
function (reject) {}
);
expect(spy).to.have.callCount(1);
expect(spy).to.have.been.calledWith(connectionWithExternalAuth);
describe('OracleDb externalAuth', function () {
const knexInstance = knex({
client: 'oracledb',
connection: {
user: 'user',
password: 'password',
connectString: 'connect-string',
externalAuth: true,
host: 'host',
database: 'database',
},
});
let spy;
before(function () {
spy = sinon.spy(knexInstance.client.driver, 'getConnection');
});
it('externalAuth and connectString should be sent to the getConnection', function () {
const connectionWithExternalAuth = {
connectString: 'connect-string',
externalAuth: true,
};
knexInstance.client.acquireRawConnection().then(
function (resolve) {},
function (reject) {}
);
expect(spy).to.have.callCount(1);
expect(spy).to.have.been.calledWith(connectionWithExternalAuth);
});
after(function () {
knexInstance.client.driver.getConnection.restore();
});
});
after(function () {
knexInstance.client.driver.getConnection.restore();
});
});
describe('OracleDb parameters', function () {
describe('with fetchAsString parameter ', function () {
let knexClient;
describe('OracleDb parameters', function () {
describe('with fetchAsString parameter ', function () {
before(function () {
const conf = _.clone(config.oracledb);
conf.fetchAsString = ['number', 'DATE', 'cLOb', 'BUFFER'];
knexClient = knex(conf);
return knexClient;
});
it('on float', function () {
return knexClient
.raw('select 7.329 as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.a('string');
});
});
it('on date', function () {
return knexClient
.raw('select CURRENT_DATE as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.a('string');
});
});
it('on clob', function () {
return knexClient
.raw('select TO_CLOB(\'LONG CONTENT\') as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.equal('LONG CONTENT');
});
});
it('on raw', function () {
return knexClient
.raw('select UTL_RAW.CAST_TO_RAW(3) as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.equal('33');
});
});
after(function () {
return knexClient.destroy();
});
});
describe('without fetchAsString parameter', function () {
let knexClient;
before(function () {
knexClient = knex(config.oracledb);
return knexClient;
});
it('on float', function () {
return knexClient
.raw('select 7.329 as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.not.be.a('string');
});
});
it('on date', function () {
return knexClient
.raw('select CURRENT_DATE as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.not.be.a('string');
});
});
it('on blob', async () => {
const result = await knexClient.raw(
'select TO_BLOB(\'67c1a1acaaca11a1b36fa6636166709b\') as "field" from dual'
);
expect(result[0]).to.be.ok;
expect(result[0].field.toString('hex')).to.be.equal(
'67c1a1acaaca11a1b36fa6636166709b'
);
});
it('on raw', function () {
return knexClient
.raw('select UTL_RAW.CAST_TO_RAW(3) as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.instanceOf(Buffer);
});
});
after(function () {
return knexClient.destroy();
});
});
});
describe('OracleDb unit tests', function () {
let knexClient;
before(function () {
@ -52,160 +207,59 @@ describe('OracleDb parameters', function () {
return knexClient;
});
it('on float', function () {
return knexClient
.raw('select 7.329 as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.a('string');
});
});
it('on date', function () {
return knexClient
.raw('select CURRENT_DATE as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.a('string');
});
});
it('on clob', function () {
return knexClient
.raw('select TO_CLOB(\'LONG CONTENT\') as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.equal('LONG CONTENT');
});
});
it('on raw', function () {
return knexClient
.raw('select UTL_RAW.CAST_TO_RAW(3) as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.equal('33');
});
});
after(function () {
return knexClient.destroy();
});
});
describe('without fetchAsString parameter', function () {
let knexClient;
before(function () {
knexClient = knex(config.oracledb);
return knexClient;
});
it('on float', function () {
return knexClient
.raw('select 7.329 as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.not.be.a('string');
});
});
it('on date', function () {
return knexClient
.raw('select CURRENT_DATE as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.not.be.a('string');
});
});
it('on blob', async () => {
const result = await knexClient.raw(
'select TO_BLOB(\'67c1a1acaaca11a1b36fa6636166709b\') as "field" from dual'
);
expect(result[0]).to.be.ok;
expect(result[0].field.toString('hex')).to.be.equal(
'67c1a1acaaca11a1b36fa6636166709b'
);
});
it('on raw', function () {
return knexClient
.raw('select UTL_RAW.CAST_TO_RAW(3) as "field" from dual')
.then(function (result) {
expect(result[0]).to.be.ok;
expect(result[0].field).to.be.instanceOf(Buffer);
});
});
after(function () {
return knexClient.destroy();
});
});
});
describe('OracleDb unit tests', function () {
let knexClient;
before(function () {
const conf = _.clone(config.oracledb);
conf.fetchAsString = ['number', 'DATE', 'cLOb', 'BUFFER'];
knexClient = knex(conf);
return knexClient;
});
it('disposes the connection on connection error', async function () {
let spy = sinon.spy();
// call the real acquireConnection but release the connection immediately to cause connection error
const acquireConnection = knexClient.client.acquireConnection;
sinon.stub(knexClient.client, 'acquireConnection').callsFake(async () => {
const conn = await acquireConnection.call(knexClient.client);
conn.release();
spy = sinon.spy(conn, 'close');
return conn;
});
let exception;
try {
await knexClient.raw('insert into DUAL values(1)');
} catch (e) {
exception = e;
}
expect(exception).not.to.equal(undefined);
expect(exception.message).to.include('NJS-003: invalid connection');
expect(spy.callCount).to.equal(1);
});
it('clears the connection from the pool on disconnect during commit', async function () {
const err = 'error message';
const spy = sinon.spy(knexClient.client, 'releaseConnection');
// call the real acquireConnection but ensure commitAsync fails simulating a disconnect
const acquireConnection = knexClient.client.acquireConnection;
sinon.stub(knexClient.client, 'acquireConnection').callsFake(async () => {
const conn = await acquireConnection.call(knexClient.client);
conn.commitAsync = () => Promise.reject(err);
return conn;
});
let exception;
try {
await knexClient.transaction(async (trx) => {
await trx('DUAL').select('*');
it('disposes the connection on connection error', async function () {
let spy = sinon.spy();
// call the real acquireConnection but release the connection immediately to cause connection error
const acquireConnection = knexClient.client.acquireConnection;
sinon.stub(knexClient.client, 'acquireConnection').callsFake(async () => {
const conn = await acquireConnection.call(knexClient.client);
conn.release();
spy = sinon.spy(conn, 'close');
return conn;
});
} catch (e) {
exception = e;
}
expect(spy.callCount).to.equal(1);
expect(exception).to.equal(err);
});
let exception;
try {
await knexClient.raw('insert into DUAL values(1)');
} catch (e) {
exception = e;
}
afterEach(function () {
knexClient.client.acquireConnection.restore();
});
expect(exception).not.to.equal(undefined);
expect(exception.message).to.include('NJS-003: invalid connection');
expect(spy.callCount).to.equal(1);
});
after(function () {
return knexClient.destroy();
it('clears the connection from the pool on disconnect during commit', async function () {
const err = 'error message';
const spy = sinon.spy(knexClient.client, 'releaseConnection');
// call the real acquireConnection but ensure commitAsync fails simulating a disconnect
const acquireConnection = knexClient.client.acquireConnection;
sinon.stub(knexClient.client, 'acquireConnection').callsFake(async () => {
const conn = await acquireConnection.call(knexClient.client);
conn.commitAsync = () => Promise.reject(err);
return conn;
});
let exception;
try {
await knexClient.transaction(async (trx) => {
await trx('DUAL').select('*');
});
} catch (e) {
exception = e;
}
expect(spy.callCount).to.equal(1);
expect(exception).to.equal(err);
});
afterEach(function () {
knexClient.client.acquireConnection.restore();
});
after(function () {
return knexClient.destroy();
});
});
});

View File

@ -0,0 +1,31 @@
const { expect } = require('chai');
const Formatter = require('../../../lib/formatter');
const Client = require('../../../lib/client');
describe('formatter', () => {
const queryContext = () => {
return {};
};
const client = new Client({ client: 'generic' });
const formatter = new Formatter(client, { queryContext });
it('correctly handles single column value', () => {
const columns = formatter.columnize('columnName');
expect(columns).to.equal('"columnName"');
});
it('correctly handles multiple column values', () => {
const columns = formatter.columnize(['columnName1', 'columnName2']);
expect(columns).to.equal('"columnName1", "columnName2"');
});
it('correctly handles null', () => {
const columns = formatter.columnize(null);
expect(columns).to.equal('');
});
it('correctly handles empty array', () => {
const columns = formatter.columnize([]);
expect(columns).to.equal('');
});
});