@mysql/xdevapi
Version:
MySQL Connector/Node.js - A Node.js driver for MySQL using the X Protocol and X DevAPI.
898 lines (717 loc) • 81.5 kB
JavaScript
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0, as
* published by the Free Software Foundation.
*
* This program is also distributed with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an
* additional permission to link the program and your derivative works
* with the separately licensed software that they have included with
* MySQL.
*
* Without limiting anything contained in the foregoing, this file,
* which is part of MySQL Connector/Node.js, is also subject to the
* Universal FOSS Exception, version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
'use strict';
/* eslint-env node, mocha */
const config = require('../../../config');
const errors = require('../../../../lib/constants/errors');
const expect = require('chai').expect;
const fixtures = require('../../../fixtures');
const mysqlx = require('../../../../');
const os = require('os');
const path = require('path');
const qs = require('querystring');
const sqlExecute = require('../../../../lib/DevAPI/SqlExecute');
const tls = require('tls');
const util = require('util');
describe('connection failures', () => {
const baseConfig = { schema: undefined };
context('while creating a new session', () => {
const failureConfig = { host: undefined, port: undefined, socket: undefined };
context('using a standalone connection', () => {
context('when the connection definition is not valid', () => {
it('fails using something other then an object or string', () => {
return mysqlx.getSession(false)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.not.equal('expect.fail()');
});
});
it('fails using a null configuration object', () => {
return mysqlx.getSession(null)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.not.equal('expect.fail()');
});
});
it('fails using an empty string', () => {
return mysqlx.getSession('')
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.not.equal('expect.fail()');
});
});
});
context('when the connection options are specified with a configuration object', () => {
it('fails when the path to the CA file is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { ca: false } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CA_PATH);
});
});
it('fails when the path to the CRL file is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { crl: false } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CRL_PATH);
});
});
it('fails when the list of TLS versions is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { versions: 'foo' } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, 'foo'));
});
});
it('fails when the list of TLS versions is empty', () => {
const emptyTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: [] } });
return mysqlx.getSession(emptyTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
});
it('fails when the list does not contain any allowed TLS version', () => {
const nonAllowedTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { versions: ['foo', 'TLSv1.1'] } });
return mysqlx.getSession(nonAllowedTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1.1', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when the list contains only invalid TLS versions', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { versions: ['foo', 'bar'] } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION, 'foo', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when the list contains only insecure TLS versions', () => {
const insecureTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { versions: ['TLSv1', 'TLSv1.1'] } });
return mysqlx.getSession(insecureTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when all the TLS versions provided in the list are not supported by the client', function () {
// This test only makes sense on Node.js v10 (or lower).
if (tls.DEFAULT_MAX_VERSION && tls.DEFAULT_MAX_VERSION !== 'TLSv1.2') {
return this.skip();
}
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { versions: ['TLSv1.3'] } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
});
it('fails when the list of ciphersuites is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { ciphersuites: 'foo' } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, 'foo'));
});
});
it('fails when the list of ciphersuites does not contain any valid one', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, failureConfig, { tls: { ciphersuites: ['foo'] } });
return mysqlx.getSession(invalidTLSConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_CIPHERSUITE);
});
});
it('fails when the connection timeout is not a number above or equal to 0', () => {
const invalidTimeoutConfig = Object.assign({}, config, baseConfig, failureConfig, { connectTimeout: 'foo1' });
return mysqlx.getSession(invalidTimeoutConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_TIMEOUT);
});
});
it('fails when the connection attributes are null/empty', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, failureConfig, { connectionAttributes: null });
return mysqlx.getSession(invalidAttributesConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
});
});
it('fails when the connection attributes are badly specified', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, failureConfig, { connectionAttributes: ['foo', 'bar'] });
return mysqlx.getSession(invalidAttributesConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
});
});
it('fails when the name of any connection attribute starts with "_"', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, failureConfig, { connectionAttributes: { _foo: 'bar' } });
return mysqlx.getSession(invalidAttributesConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTE_NAME);
});
});
it('fails when SRV resolution is badly configured', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: 'foo' });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SRV_LOOKUP_OPTION);
});
});
it('fails when enabling SRV resolution whilst specifying multiple hosts', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: true, endpoints: [{ host: 'foo' }, { host: 'bar' }] });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_MULTIPLE_ENDPOINTS);
});
});
it('fails when enabling SRV resolution whilst specifying a port', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: true, port: 33061 });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_PORT);
});
});
it('fails when enabling SRV resolution whist specifying a port using the list of endpoints', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: true, endpoints: [{ host: 'foo', port: 33062 }] });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_PORT);
});
});
it('fails when enabling SRV resolution whilst specifying a socket', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: true, socket: '/path/to/socket' });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_UNIX_SOCKET);
});
});
it('fails when enabling SRV resolution whilst specifying a socket using the list of endpoints', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, failureConfig, { resolveSrv: true, endpoints: [{ socket: '/path/to/socket' }] });
return mysqlx.getSession(invalidSRVConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_UNIX_SOCKET);
});
});
it('fails when the port is not a number between 0 and 65536', () => {
const invalidPortConfig = Object.assign({}, config, baseConfig, failureConfig, { port: -1 });
return mysqlx.getSession(invalidPortConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
});
});
it('fails when a port in the list of endpoints is not a number between 0 and 65536', () => {
const invalidPortConfig = Object.assign({}, config, baseConfig, failureConfig, { endpoints: [{ port: 33060 }, { port: -1 }] });
return mysqlx.getSession(invalidPortConfig)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
});
});
});
context('when the connection options are specified with a connection string', () => {
it('fails when certificate authority verification is enabled but the path to the certificate authority file is not provided', () => {
const tlsMode = 'VERIFY_CA';
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { enabled: true } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?ssl-mode=${tlsMode}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_CERTIFICATE_AUTHORITY_REQUIRED, tlsMode));
});
});
it('fails when hostname verification is enabled but the path to the certificate authority file is not provided', () => {
const tlsMode = 'VERIFY_IDENTITY';
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { enabled: true } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?ssl-mode=${tlsMode}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_CERTIFICATE_AUTHORITY_REQUIRED, tlsMode));
});
});
it('fails when the list of TLS versions is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: 'foo' } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=foo`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, 'foo'));
});
});
it('fails when the list of TLS versions is empty', () => {
const emptyTLSConfig = Object.assign({}, config, baseConfig);
const uri = `mysqlx://${emptyTLSConfig.user}:${emptyTLSConfig.password}@${emptyTLSConfig.host}:${emptyTLSConfig.port}?tls-versions=[]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
});
it('fails when the list does not contain any allowed TLS version', () => {
const nonAllowedTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['foo', 'TLSv1.1'] } });
const uri = `mysqlx://${nonAllowedTLSConfig.user}:${nonAllowedTLSConfig.password}@${nonAllowedTLSConfig.host}:${nonAllowedTLSConfig.port}?tls-versions=[${nonAllowedTLSConfig.tls.versions.join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1.1', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when the list contains only invalid TLS versions', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['foo', 'bar'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION, 'foo', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when the list contains only insecure TLS versions', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['TLSv1', 'TLSv1.1'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
});
it('fails when all the TLS versions provided in the list are not supported by the client', function () {
// This test only makes sense on Node.js v10 (or lower).
if (tls.DEFAULT_MAX_VERSION && tls.DEFAULT_MAX_VERSION !== 'TLSv1.2') {
return this.skip();
}
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['TLSv1.3'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
});
it('fails when the list of ciphersuites is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { ciphersuites: 'foo' } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-ciphersuites=${invalidTLSConfig.tls.ciphersuites}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, 'foo'));
});
});
it('fails when the list of ciphersuites does not contain any valid one', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { ciphersuites: ['foo'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-ciphersuites=[${invalidTLSConfig.tls.ciphersuites.join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_CIPHERSUITE);
});
});
it('fails when the connection timeout is not a number above or equal to 0', () => {
const invalidTimeoutConfig = Object.assign({}, config, baseConfig, { connectTimeout: -1 });
const uri = `mysqlx://${invalidTimeoutConfig.user}:${invalidTimeoutConfig.password}@${invalidTimeoutConfig.host}:${invalidTimeoutConfig.port}?connect-timeout=${invalidTimeoutConfig.connectTimeout}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_TIMEOUT);
});
});
it('fails when the connection attributes are badly specified', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: ['foo', 'bar'] });
const uri = `mysqlx://${invalidAttributesConfig.user}:${invalidAttributesConfig.password}@${invalidAttributesConfig.host}:${invalidAttributesConfig.port}?connection-attributes=${invalidAttributesConfig.connectionAttributes.join(',')}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
});
});
it('fails when the name of any connection attribute starts with "_"', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: { _foo: 'bar' } });
const uri = `mysqlx://${invalidAttributesConfig.user}:${invalidAttributesConfig.password}@${invalidAttributesConfig.host}:${invalidAttributesConfig.port}?connection-attributes=[${qs.stringify(invalidAttributesConfig.connectionAttributes)}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTE_NAME);
});
});
it('fails when enabling SRV resolution whilst specifying multiple hosts', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, endpoints: [{ host: 'foo' }, { host: 'bar' }] });
const uri = `mysqlx+srv://${invalidSRVConfig.user}:${invalidSRVConfig.password}@[${invalidSRVConfig.endpoints.map(e => e.host).join(',')}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_MULTIPLE_ENDPOINTS);
});
});
it('fails when enabling SRV resolution whilst specifying a port', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, port: 33061 });
const uri = `mysqlx+srv://${invalidSRVConfig.user}:${invalidSRVConfig.password}@${invalidSRVConfig.host}:${invalidSRVConfig.port}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_PORT);
});
});
it('fails when enabling SRV resolution whilst specifying a socket', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, socket: '/path/to/socket' });
const uri = `mysqlx+srv://${invalidSRVConfig.user}:${invalidSRVConfig.password}@${encodeURIComponent(invalidSRVConfig.socket)}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_UNIX_SOCKET);
});
});
it('fails when the port is not a number between 0 and 65536', () => {
const invalidPortConfig = Object.assign({}, config, baseConfig, { port: -1 });
const uri = `mysqlx://${invalidPortConfig.user}:${invalidPortConfig.password}@${invalidPortConfig.host}:${invalidPortConfig.port}`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
});
});
it('fails when a port in the list of endpoints is not a number between 0 and 65536', () => {
const invalidPortConfig = Object.assign({}, config, baseConfig, failureConfig, { endpoints: [{ port: 33060 }, { port: -1 }] });
const uri = `mysqlx://${invalidPortConfig.user}:${invalidPortConfig.password}@[${invalidPortConfig.endpoints.map(e => `${invalidPortConfig.host}:${e.port}`)}]`;
return mysqlx.getSession(uri)
.then(() => {
return expect.fail();
})
.catch(err => {
return expect(err.message).to.equal(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
});
});
});
});
context('using a connection pool', () => {
it('throws an error when unknown client options are provided', () => {
const unknownClientOptions = { foo: 'bar' };
expect(() => mysqlx.getClient({}, unknownClientOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION, 'foo'));
});
it('throws an error when unknown pooling options are provided', () => {
const unknownPoolingOptions = { pooling: { foo: 'bar' } };
expect(() => mysqlx.getClient({}, unknownPoolingOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION, 'pooling.foo'));
});
it('throws an error when invalid pooling option values are provided', () => {
let unknownPoolingOptions = { pooling: { enabled: 'foo' } };
expect(() => mysqlx.getClient({}, unknownPoolingOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION_VALUE, 'pooling.enabled', 'foo'));
unknownPoolingOptions = { pooling: { maxIdleTime: -1 } };
expect(() => mysqlx.getClient({}, unknownPoolingOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION_VALUE, 'pooling.maxIdleTime', -1));
unknownPoolingOptions = { pooling: { maxSize: -1 } };
expect(() => mysqlx.getClient({}, unknownPoolingOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION_VALUE, 'pooling.maxSize', -1));
unknownPoolingOptions = { pooling: { maxSize: -1 } };
expect(() => mysqlx.getClient({}, unknownPoolingOptions))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_CLIENT_OPTION_VALUE, 'pooling.maxSize', -1));
});
context('when the connection options are specified with a configuration object', () => {
it('throws an error when the path to the CA file is badly specified', () => {
let invalidTLSConfig = { tls: { ca: false } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CA_PATH);
invalidTLSConfig = { tls: { ca: [] } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CA_PATH);
});
it('throws an error when the path to the CRL file is badly specified', () => {
let invalidTLSConfig = { tls: { crl: {} } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CRL_PATH);
invalidTLSConfig = { tls: { crl: 2 } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CRL_PATH);
});
it('throws an error when the list of TLS versions is badly specified', () => {
let invalidTLSConfig = { tls: { versions: {} } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, {}));
invalidTLSConfig = { tls: { versions: 'foo' } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, 'foo'));
});
it('throws an error when the list of TLS versions is empty', () => {
const emptyTLSConfig = { tls: { versions: [] } };
expect(() => mysqlx.getClient(emptyTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
it('throws an error when the list does not contain any allowed TLS version', () => {
const nonAllowedTLSConfig = { tls: { versions: ['foo', 'TLSv1'] } };
expect(() => mysqlx.getClient(nonAllowedTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when the list contains only invalid TLS versions', () => {
const invalidTLSConfig = { tls: { versions: ['foo', 'bar'] } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION, 'foo', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when the list contains only insecure TLS versions', () => {
const insecureTLSConfig = { tls: { versions: ['TLSv1', 'TLSv1.1'] } };
expect(() => mysqlx.getClient(insecureTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when all the TLS versions provided in the list are not supported by the client', function () {
if (tls.DEFAULT_MAX_VERSION === 'TLSv1.3') {
return this.skip();
}
const invalidTLSConfig = { tls: { versions: ['TLSv1.3'] } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION));
});
it('throws an error when the list of ciphersuites is badly specified', () => {
let invalidTLSConfig = { tls: { ciphersuites: 'foo' } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, 'foo'));
invalidTLSConfig = { tls: { ciphersuites: {} } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, {}));
invalidTLSConfig = { tls: { ciphersuites: false } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, 'false'));
});
it('throws an error when the list of ciphersuites does not contain any valid one', () => {
const invalidTLSConfig = { tls: { ciphersuites: ['foo', 'bar'] } };
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_CIPHERSUITE);
});
it('throws an error when the connection timeout is not a number above or equal to 0', () => {
let invalidTimeoutConfig = Object.assign({}, config, baseConfig, { connectTimeout: 'foo1' });
expect(() => mysqlx.getClient(invalidTimeoutConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_TIMEOUT);
invalidTimeoutConfig = Object.assign({}, config, baseConfig, { connectTimeout: -1 });
expect(() => mysqlx.getClient(invalidTimeoutConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_TIMEOUT);
});
it('throws an error when the connection attributes are badly specified', () => {
let invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: ['foo', 'bar'] });
expect(() => mysqlx.getClient(invalidAttributesConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: 'foo' });
expect(() => mysqlx.getClient(invalidAttributesConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: null });
expect(() => mysqlx.getClient(invalidAttributesConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTES_DEFINITION);
});
it('throws an error when the name of any connection attribute starts with "_"', () => {
const invalidAttributesConfig = Object.assign({}, config, baseConfig, { connectionAttributes: { _foo: 'bar' } });
expect(() => mysqlx.getClient(invalidAttributesConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_SESSION_ATTRIBUTE_NAME);
});
it('throws an error when SRV resolution is badly configured', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: 'foo' });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_SRV_LOOKUP_OPTION);
});
it('throws an error when enabling SRV resolution whilst specifying multiple hosts', () => {
const invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, endpoints: [{ host: 'foo' }, { host: 'bar' }] });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_MULTIPLE_ENDPOINTS);
});
it('throws an error when enabling SRV resolution whilst specifying a port', () => {
let invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, port: 33061 });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_PORT);
invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, endpoints: [{ host: 'foo', port: 33062 }] });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_PORT);
});
it('throws an error when enabling SRV resolution whilst specifying a socket', () => {
let invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, socket: '/path/to/socket', port: undefined });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_UNIX_SOCKET);
invalidSRVConfig = Object.assign({}, config, baseConfig, { resolveSrv: true, endpoints: [{ socket: '/path/to/socket' }] });
expect(() => mysqlx.getClient(invalidSRVConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_SRV_LOOKUP_NO_UNIX_SOCKET);
});
it('throws an error when the port is not a number between 0 and 65536', () => {
let invalidPortConfig = Object.assign({}, config, baseConfig, { port: -1 });
expect(() => mysqlx.getClient(invalidPortConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
invalidPortConfig = Object.assign({}, config, baseConfig, { port: 'foo' });
expect(() => mysqlx.getClient(invalidPortConfig))
.to.throw(errors.MESSAGES.ER_DEVAPI_BAD_CONNECTION_PORT_RANGE);
});
});
context('when the connection options are specified with a connection string', () => {
it('throws an error when the list of TLS versions is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: {} } });
let uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=${JSON.stringify(invalidTLSConfig.tls.versions)}`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, JSON.stringify({})));
invalidTLSConfig.tls.versions = 'foo';
uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=${invalidTLSConfig.tls.versions}`;
expect(() => mysqlx.getClient(invalidTLSConfig))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION_LIST, 'foo'));
});
it('throws an error when the list of TLS versions is empty', () => {
const emptyTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: [] } });
const uri = `mysqlx://${emptyTLSConfig.user}:${emptyTLSConfig.password}@${emptyTLSConfig.host}:${emptyTLSConfig.port}?tls-versions=${JSON.stringify(emptyTLSConfig.tls.versions)}`;
expect(() => mysqlx.getClient(uri))
.to.throw(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION);
});
it('throws an error when the list does not contain any allowed TLS version', () => {
const nonAllowedTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['foo', 'TLSv1'] } });
const uri = `mysqlx://${nonAllowedTLSConfig.user}:${nonAllowedTLSConfig.password}@${nonAllowedTLSConfig.host}:${nonAllowedTLSConfig.port}?tls-versions=[${nonAllowedTLSConfig.tls.versions.join(',')}]`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when the list contains only invalid TLS versions', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['foo', 'bar'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_VERSION, 'foo', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when the list contains only insecure TLS versions', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['TLSv1', 'TLSv1.1'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_INSECURE_TLS_VERSIONS, 'TLSv1', 'TLSv1.2, TLSv1.3'));
});
it('throws an error when all the TLS versions provided in the list are not supported by the client', function () {
if (tls.DEFAULT_MAX_VERSION === 'TLSv1.3') {
return this.skip();
}
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { versions: ['TLSv1.3'] } });
const uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-versions=[${invalidTLSConfig.tls.versions.join(',')}]`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_NO_SUPPORTED_TLS_VERSION));
});
it('throws an error when the list of ciphersuites is badly specified', () => {
const invalidTLSConfig = Object.assign({}, config, baseConfig, { tls: { ciphersuites: 'foo' } });
let uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-ciphersuites=${invalidTLSConfig.tls.ciphersuites}`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, 'foo'));
invalidTLSConfig.tls.ciphersuites = {};
uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-ciphersuites=${JSON.stringify(invalidTLSConfig.tls.ciphersuites)}`;
expect(() => mysqlx.getClient(uri))
.to.throw(util.format(errors.MESSAGES.ER_DEVAPI_BAD_TLS_CIPHERSUITE_LIST, JSON.stringify({})));
invalidTLSConfig.tls.ciphersuites = false;
uri = `mysqlx://${invalidTLSConfig.user}:${invalidTLSConfig.password}@${invalidTLSConfig.host}:${invalidTLSConfig.port}?tls-ciphersuites=${invalidTLSConfig.tls.