UNPKG

@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
/* * 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.