diff --git a/Jenkinsfile b/Jenkinsfile index afa02be10..7d7b74a93 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,7 @@ def initializeEnvironment() { - def nodeVersions = ['16': '16.20.2', '18': '18.17.1', '20': '20.5.1'] + def nodeVersions = ['18': '18.20.4', '20': '20.17.0', '22': '22.8.0'] env.DRIVER_DISPLAY_NAME = 'Cassandra Node.js Driver' env.DRIVER_METRIC_TYPE = 'oss' if (env.GIT_URL.contains('riptano/nodejs-driver')) { @@ -19,6 +19,9 @@ def initializeEnvironment() { env.GITHUB_COMMIT_URL = "${GITHUB_PROJECT_URL}/commit/${env.GIT_COMMIT}" env.NODEJS_VERSION_FULL = nodeVersions[env.NODEJS_VERSION] + env.JAVA8_HOME="${JABBA_HOME}/jdk/1.8" + env.JAVA11_HOME="${JABBA_HOME}/jdk/openjdk@1.11" + sh label: 'Assign Node.js global environment', script: '''#!/bin/bash -lex nodenv versions echo "Using Node.js runtime ${NODEJS_VERSION} (${NODEJS_VERSION_FULL})" @@ -38,13 +41,25 @@ CCM_CASSANDRA_VERSION=${DSE_FIXED_VERSION} # maintain for backwards compatibilit CCM_VERSION=${DSE_FIXED_VERSION} CCM_SERVER_TYPE=dse DSE_VERSION=${DSE_FIXED_VERSION} -CCM_IS_DSE=true +CCM_DISTRIBUTION=dse CCM_BRANCH=${DSE_FIXED_VERSION} DSE_BRANCH=${DSE_FIXED_VERSION} ENVIRONMENT_EOF ''' } + if (env.CASSANDRA_VERSION.split('-')[0] == 'hcd'){ + env.HCD_FIXED_VERSION = env.CASSANDRA_VERSION.split('-')[1] + sh label: 'Update environment for HCD', script: '''#!/bin/bash -le + cat >> ${HOME}/environment.txt << ENVIRONMENT_EOF +CCM_PATH=${HOME}/ccm +CCM_CASSANDRA_VERSION=${HCD_FIXED_VERSION} # maintain for backwards compatibility +CCM_VERSION=${HCD_FIXED_VERSION} +CCM_DISTRIBUTION=hcd +ENVIRONMENT_EOF + ''' + } + sh label: 'Display Node.js and environment information', script: '''#!/bin/bash -le # Load CCM environment variables set -o allexport @@ -201,6 +216,12 @@ def describeAdhocTestingStage() { } } +def describeInstallAndLint(){ + describePerCommitStage() + installDriverAndDependencies() + executeLinter() +} + // branch pattern for cron def branchPatternCron() { ~"(master)" @@ -240,15 +261,19 @@ pipeline { ''') choice( name: 'ADHOC_BUILD_AND_EXECUTE_TESTS_NODEJS_VERSION', - choices: ['16', '18', '20', 'ALL'], + choices: ['18', '20', '22', 'ALL'], description: 'Node.js version to use for adhoc BUILD-AND-EXECUTE-TESTS ONLY!') choice( name: 'ADHOC_BUILD_AND_EXECUTE_TESTS_SERVER_VERSION', choices: [ - '3.11', // Current Apache Cassandra - '4.0', // Development Apache Cassandra + '3.11', // Previous Apache Cassandra + '4.0', // Previous Apache Cassandra + '4.1', // Previous Apache Cassandra + '5.0', // Current Apache Cassandra 'dse-5.1.35', // Legacy DataStax Enterprise - 'dse-6.8.30', // Development DataStax Enterprise + 'dse-6.8.30', // Previoius DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0', // HCD 'ALL'], description: '''Apache Cassandra and DataStax Enterprise server version to use for adhoc BUILD-AND-EXECUTE-TESTS ONLY! @@ -326,14 +351,16 @@ pipeline { axes { axis { name 'CASSANDRA_VERSION' - values '3.11', // Current Apache Cassandra - '4.0', // Development Apache Cassandra - 'dse-5.1.35', // Legacy DataStax Enterprise - 'dse-6.8.30' // Development DataStax Enterprise + values '3.11', // Previous Apache Cassandra + '4.1', // Previous Apache Cassandra + '5.0', // Current Apache Cassandra + 'dse-6.8.30', // Previous DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0' // HCD } axis { name 'NODEJS_VERSION' - values '16', '18', '20' + values '18', '20', '22' } } @@ -352,19 +379,9 @@ pipeline { } } } - stage('Describe-Build') { + stage('Describe-Install-And-Lint') { steps { - describePerCommitStage() - } - } - stage('Install-Driver-And-Dependencies') { - steps { - installDriverAndDependencies() - } - } - stage('Execute-Linter') { - steps { - executeLinter() + describeInstallAndLint() } } stage('Execute-Tests') { @@ -419,14 +436,16 @@ pipeline { axes { axis { name 'CASSANDRA_VERSION' - values '3.11', // Current Apache Cassandra - '4.0', // Development Apache Cassandra - 'dse-5.1.35', // Legacy DataStax Enterprise - 'dse-6.8.30' // Development DataStax Enterprise + values '3.11', // Previous Apache Cassandra + '4.1', // Previous Apache Cassandra + '5.0', // Current Apache Cassandra + 'dse-6.8.30', // Previous DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0' // HCD } axis { name 'NODEJS_VERSION' - values '16', '18', '20' + values '18', '20', '22' } } @@ -445,19 +464,9 @@ pipeline { } } } - stage('Describe-Build') { - steps { - describeScheduledTestingStage() - } - } - stage('Install-Driver-And-Dependencies') { - steps { - installDriverAndDependencies() - } - } - stage('Execute-Linter') { + stage('Describe-Install-And-Lint') { steps { - executeLinter() + describeInstallAndLint() } } stage('Execute-Tests') { @@ -486,12 +495,6 @@ pipeline { aborted { notifySlack('aborted') } - success { - notifySlack('completed') - } - unstable { - notifySlack('unstable') - } failure { notifySlack('FAILED') } @@ -510,14 +513,17 @@ pipeline { axes { axis { name 'CASSANDRA_VERSION' - values '3.11', // Current Apache Cassandra - '4.0', // Development Apache Cassandra - 'dse-5.1.35', // Legacy DataStax Enterprise - 'dse-6.8.30' // Development DataStax Enterprise + values '3.11', // Previous Apache Cassandra + '4.1', // Previous Apache Cassandra + '5.0', // Current Apache Cassandra + 'dse-6.8.30', // Previous DataStax Enterprise + 'dse-6.9.0', // Current DataStax Enterprise + 'hcd-1.0.0' // HCD + } axis { name 'NODEJS_VERSION' - values '16', '18', '20' + values '18', '20', '22' } } when { diff --git a/package-lock.json b/package-lock.json index c30bacf49..d35a5d25c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,22 +9,22 @@ "version": "4.7.2", "license": "Apache-2.0", "dependencies": { - "@types/long": "^5.0.0", - "@types/node": ">=8", - "adm-zip": "^0.5.10", - "long": "^5.2.3" + "@types/long": "~5.0.0", + "@types/node": "^18.11.18", + "adm-zip": "~0.5.10", + "long": "~5.2.3" }, "devDependencies": { - "chai": "^4.3.8", - "kerberos": "^2.0.3", - "mocha": "^10.2.0", - "mocha-jenkins-reporter": "^0.4.8", + "chai": "~4.3.8", + "kerberos": "~2.0.3", + "mocha": "~10.2.0", + "mocha-jenkins-reporter": "~0.4.8", "proxyquire": "~2.1.3", - "sinon": "^15.2.0", + "sinon": "~15.2.0", "temp": ">= 0.8.3" }, "engines": { - "node": ">=8" + "node": ">=16" } }, "node_modules/@sinonjs/commons": { @@ -81,9 +81,12 @@ } }, "node_modules/@types/node": { - "version": "20.5.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", - "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/adm-zip": { "version": "0.5.10", @@ -1712,6 +1715,11 @@ "node": ">=4" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 6fbfd049a..7b116e2f4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "types": "./index.d.ts", "dependencies": { "@types/long": "~5.0.0", - "@types/node": ">=8", + "@types/node": "^18.11.18", "adm-zip": "~0.5.10", "long": "~5.2.3" }, diff --git a/test/integration/short/control-connection-tests.js b/test/integration/short/control-connection-tests.js index e2b3d260e..b48102160 100644 --- a/test/integration/short/control-connection-tests.js +++ b/test/integration/short/control-connection-tests.js @@ -121,7 +121,7 @@ describe('ControlConnection', function () { // While the host is started, it's not a given that it will have been connected and marked up, // wait for that to be the case. - await helper.wait.forNodeToBeAdded(cc.hosts, 3); + await helper.wait.forNodeToBeAdded(cc.hosts, 3, 5000, 200); await helper.wait.forNodeUp(cc.hosts, 3); const countUp = cc.hosts.values().reduce((value, host) => value + (host.isUp() ? 1 : 0), 0); @@ -220,7 +220,7 @@ describe('ControlConnection', function () { // restart node 2 and make sure it comes up. await util.promisify(helper.ccmHelper.startNode)(2); - await helper.wait.forNodeUp(cc.hosts, 2); + await helper.wait.forNodeUp(cc.hosts, 2, 5000, 200); // check that host 1 is down, host 2 is up and the control connection is to host 2. cc.hosts.forEach(h => { @@ -232,7 +232,7 @@ describe('ControlConnection', function () { } }); - await helper.wait.until(() => cc.host); + await helper.wait.until(() => cc.host, 5000, 200); assert.strictEqual(helper.lastOctetOf(cc.host), '2'); }); diff --git a/test/integration/short/geometry/polygon-tests.js b/test/integration/short/geometry/polygon-tests.js index 6fd81830d..41715487e 100644 --- a/test/integration/short/geometry/polygon-tests.js +++ b/test/integration/short/geometry/polygon-tests.js @@ -130,7 +130,12 @@ vdescribe('dse-5.0', 'Polygon @SERVER_API', function () { normalizedCoordinates.push([c[0], c[2], c[1], c[3]]); } } - assert.deepEqual(normalizedCoordinates, polygon.toJSON().coordinates); + if (helper.isDseGreaterThan('6.9')) { + //TODO: find documentation + assert.deepEqual(value, polygon.toJSON()); + }else{ + assert.deepEqual(normalizedCoordinates, polygon.toJSON().coordinates); + } eachNext(); }); }); diff --git a/test/integration/short/graph/graph-olap-tests.js b/test/integration/short/graph/graph-olap-tests.js index 87292fffd..41789bf1b 100644 --- a/test/integration/short/graph/graph-olap-tests.js +++ b/test/integration/short/graph/graph-olap-tests.js @@ -134,15 +134,21 @@ vdescribe('dse-5.0', 'Client with spark workload', function () { {loadBalancing: new DefaultLoadBalancingPolicy(), graphOptions: {source: 'a'}})]}, true) ); context('with no callback specified', function () { - it('should return a promise for OLAP query', function () { - const client = newInstance(); - const p = client.executeGraph('g.V().count()', { graphSource: 'a' }); - helper.assertInstanceOf(p, Promise); - return p.then(function (result) { - helper.assertInstanceOf(result, graphModule.GraphResultSet); - assert.strictEqual(typeof result.first(), 'number'); - }); - }); + // if dse-6.9.0, skip this test because NODEJS-676 and DSP-24336 + if (helper.getServerInfo().version === '6.9.0' && helper.getServerInfo().isDse) { + xit('should return a promise for OLAP query'); + } else { + it( + 'should return a promise for OLAP query', function () { + const client = newInstance(); + const p = client.executeGraph('g.V().count()', { graphSource: 'a' }); + helper.assertInstanceOf(p, Promise); + return p.then(function (result) { + helper.assertInstanceOf(result, graphModule.GraphResultSet); + assert.strictEqual(typeof result.first(), 'number'); + }); + }); + } }); }); }); diff --git a/test/integration/short/graph/graph-tests.js b/test/integration/short/graph/graph-tests.js index 55ecada4c..81e641e71 100644 --- a/test/integration/short/graph/graph-tests.js +++ b/test/integration/short/graph/graph-tests.js @@ -449,21 +449,26 @@ vdescribe('dse-5.0', 'Client @SERVER_API', function () { done(); }); })); - it('should retrieve a Int64 scalar', wrapClient(function (client, done) { - const query = JSON.stringify({ - '@type': 'g:Bytecode', - '@value': { - 'step': [["V"], ["count"]] - } - }); - client.executeGraph(query, null, { graphLanguage: 'bytecode-json' }, function (err, result) { - assert.ifError(err); - helper.assertInstanceOf(result, graphModule.GraphResultSet); - const count = result.first(); - helper.assertInstanceOf(count, types.Long); - done(); - }); - })); + // if dse-6.9.0, skip this test because NODEJS-676 and DSP-24336 + if (helper.getServerInfo().version === '6.9.0' && helper.getServerInfo().isDse) { + it.skip('should retrieve a Int64 scalar'); + } else { + it('should retrieve a Int64 scalar', wrapClient(function (client, done) { + const query = JSON.stringify({ + '@type': 'g:Bytecode', + '@value': { + 'step': [["V"], ["count"]] + } + }); + client.executeGraph(query, null, { graphLanguage: 'bytecode-json' }, function (err, result) { + assert.ifError(err); + helper.assertInstanceOf(result, graphModule.GraphResultSet); + const count = result.first(); + helper.assertInstanceOf(count, types.Long); + done(); + }); + })); + } it('should allow graph language to be set from the execution profile', wrapClient(function (client, done) { const query = JSON.stringify({ '@type': 'g:Bytecode', @@ -480,7 +485,7 @@ vdescribe('dse-5.0', 'Client @SERVER_API', function () { }); done(); }); - }, { profiles: [ new ExecutionProfile('graph-profile1', { graphOptions: { language: 'bytecode-json' } }) ]})); + }, { profiles: [new ExecutionProfile('graph-profile1', { graphOptions: { language: 'bytecode-json' } })] })); }); }); it('should use list as a parameter', wrapClient(function(client, done) { diff --git a/test/integration/short/metadata-tests.js b/test/integration/short/metadata-tests.js index 85bb05d77..9941e1a3b 100644 --- a/test/integration/short/metadata-tests.js +++ b/test/integration/short/metadata-tests.js @@ -1028,20 +1028,37 @@ describe('metadata @SERVER_API', function () { }); }, done); }); - vit('4.0', 'should retrieve the metadata of a virtual table', () => { - const client = setupInfo.client; - return client.metadata.getTable('system_views', 'clients') - .then((table) => { - assert.ok(table); - assert.ok(table.virtual); - assert.strictEqual(table.name, 'clients'); - assert.deepEqual(table.columns.map(c => c.name), ['address', 'connection_stage', 'driver_name', - 'driver_version', 'hostname', 'port', 'protocol_version', 'request_count', 'ssl_cipher_suite', - 'ssl_enabled', 'ssl_protocol', 'username']); - assert.deepEqual(table.clusteringOrder, ['ASC']); - assert.deepEqual(table.partitionKeys.map(c => c.name), ['address']); - assert.deepEqual(table.clusteringKeys.map(c => c.name), ['port']); - }); + it('should retrieve the metadata of a virtual table', function () { + if (helper.isCassandraGreaterThan('4.0')) { + const client = setupInfo.client; + return client.metadata.getTable('system_views', 'clients') + .then((table) => { + assert.ok(table); + assert.ok(table.virtual); + assert.strictEqual(table.name, 'clients'); + assert.deepEqual(table.clusteringOrder, ['ASC']); + assert.deepEqual(table.partitionKeys.map(c => c.name), ['address']); + assert.deepEqual(table.clusteringKeys.map(c => c.name), ['port']); + + if (helper.isCassandraGreaterThan('5.0')) { + // 5.0 and above + assert.deepEqual(table.columns.map(c => c.name), ['address', 'client_options', 'connection_stage', 'driver_name', + 'driver_version', 'hostname', 'keyspace_name', 'port', 'protocol_version', 'request_count', 'ssl_cipher_suite', + 'ssl_enabled', 'ssl_protocol', 'username']); + } else if (helper.isCassandraGreaterThan('4.1')) { + // 4.1 + assert.deepEqual(table.columns.map(c => c.name), ['address', 'client_options', 'connection_stage', 'driver_name', + 'driver_version', 'hostname', 'port', 'protocol_version', 'request_count', 'ssl_cipher_suite', + 'ssl_enabled', 'ssl_protocol', 'username']); + } else { + // 4.0 + assert.deepEqual(table.columns.map(c => c.name), ['address', 'connection_stage', 'driver_name', + 'driver_version', 'hostname', 'port', 'protocol_version', 'request_count', 'ssl_cipher_suite', + 'ssl_enabled', 'ssl_protocol', 'username']); + } + }); + } + // else, lower than 4.0, skip }); it('should retrieve the updated metadata after a schema change', function (done) { const client = newInstance(); diff --git a/test/test-helper.js b/test/test-helper.js index ee5eab5dc..c342d1e32 100644 --- a/test/test-helper.js +++ b/test/test-helper.js @@ -41,7 +41,12 @@ const cassandraVersionByDse = { '5.1': '3.11', '6.0': '3.11', '6.7': '3.11', - '6.8': '3.11' + '6.8': '3.11', + '6.9': '3.11' +}; + +const cassandraVersionByHcd = { + '1.0': '4.0', }; const afterNextHandlers = []; @@ -106,7 +111,6 @@ const helper = { if (options.removeClusterAfter !== false) { after(helper.ccmHelper.remove); } - return { client: client, keyspace: keyspace @@ -395,27 +399,34 @@ const helper = { /** * Gets the Apache Cassandra version. - * When the server is DSE, gets the Apache Cassandra equivalent. + * When the server is DSE/HCD, gets the Apache Cassandra equivalent. */ getCassandraVersion: function () { const serverInfo = this.getServerInfo(); - if (!serverInfo.isDse) { + if (serverInfo.distribution === 'cassandra') { return serverInfo.version; } - - const dseVersion = serverInfo.version.split('.').slice(0, 2).join('.'); - return cassandraVersionByDse[dseVersion] || cassandraVersionByDse['6.7']; + const literalVersion = serverInfo.version.split('.').slice(0, 2).join('.'); + if (serverInfo.distribution === 'hcd') { + return cassandraVersionByHcd[literalVersion] || cassandraVersionByHcd['1.0']; + } + if (serverInfo.distribution === 'dse') { + return cassandraVersionByDse[literalVersion] || cassandraVersionByDse['6.7']; + } + throw new Error('Unknown distribution ' + serverInfo.distribution); }, /** * Gets the server version and type. - * @return {{version, isDse}} + * @return {{version: String, isDse: Boolean, isHcd: Boolean, distribution: String}} */ getServerInfo: function () { return { version: process.env['CCM_VERSION'] || '3.11.4', - isDse: process.env['CCM_IS_DSE'] === 'true' + isDse: process.env['CCM_DISTRIBUTION'] === 'dse', + isHcd: process.env['CCM_DISTRIBUTION'] === 'hcd', + distribution: process.env['CCM_DISTRIBUTION'] || 'cassandra' }; }, @@ -450,6 +461,11 @@ const helper = { return this.getServerInfo().isDse; }, + /** Determines if the current server is a HCD instance. */ + isHcd: function () { + return this.getServerInfo().isHcd; + }, + /** * Determines if the current C* or DSE instance version is greater than or equals to the C* version provided * @param {String} version The version in string format, dot separated. @@ -987,6 +1003,33 @@ const helper = { ], callback); }, + /** + * + * @param {Array.} yamlToFix + */ + fixYaml : function (yamlToFix) { + if (helper.isCassandraGreaterThan("4.1.0")) { + // fix the yaml options that turned obsolete since 4.1.0 + yamlToFix = yamlToFix.map(keyValue => { + const [key, value] = keyValue.split(':'); + const a = /^(\w+)_in_ms$/.exec(key); + if (a) { + return `${a[1]}:${value}ms`; + } + const b = /^(\w+)_in_kb$/.exec(key); + if (b) { + return `${b[1]}:${value}KiB`; + } + const c = /enable_(\w+)$/.exec(key); + if (c) { + return `${c[1]}_enabled:${value}`; + } + return keyValue; + }); + } + return yamlToFix; + }, + /** * Makes a http request and returns the body. * @param {{host, port, path}} requestOptions @@ -1103,6 +1146,10 @@ helper.ccm.startAll = function (nodeLength, options, callback) { create.push('--dse'); } + if(serverInfo.isHcd) { + create.push('--hcd'); + } + create.push('-v', serverInfo.version); if (process.env['CCM_INSTALL_DIR']) { @@ -1135,6 +1182,7 @@ helper.ccm.startAll = function (nodeLength, options, callback) { if (!options.yaml || !options.yaml.length) { return next(); } + options.yaml = helper.fixYaml(options.yaml); helper.trace('With cassandra yaml options', options.yaml); self.exec(['updateconf'].concat(options.yaml), next); }, @@ -1206,6 +1254,10 @@ helper.ccm.bootstrapNode = function (options, callback) { ccmArgs.push('--dse'); } + if (helper.getServerInfo().isHcd) { + ccmArgs.push('--hcd'); + } + if (options.dc) { ccmArgs.push('-d', options.dc); } @@ -1243,7 +1295,7 @@ helper.ccm.setWorkload = function (nodeIndex, workloads, callback) { * @param {Function} callback */ helper.ccm.startNode = function (nodeIndex, callback) { - const args = ['node' + nodeIndex, 'start', '--wait-other-notice', '--wait-for-binary-proto']; + const args = ['node' + nodeIndex, 'start', '--wait-for-binary-proto']; if (helper.isWin() && helper.isCassandraGreaterThan('2.2.4')) { args.push('--quiet-windows'); @@ -1564,7 +1616,6 @@ helper.ads.getKrb5ConfigPath = function() { return path.join(this.dir, 'krb5.conf'); }; - /** * A retry policy for testing purposes only, retries for a number of times * @param {Number} times