diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 2355eb74da..0000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,28 +0,0 @@ -environment: - PGUSER: "postgres" - PGPASSWORD: "Password12!" - MYSQL_PASSWORD: "Password12!" - -# Install scripts runs after repo cloning -install: - - ps: .\test\script\appveyor\install-redis.ps1 - - ps: .\test\script\appveyor\install-elasticsearch.ps1 - - ps: .\test\script\appveyor\install-cassandra.ps1 - - ps: Install-Product node 10 # Latest LTS - - npm install - -# Services are started after all install scripts have run -services: - - mysql - - mongodb - - postgresql - - mssql2017 - -# Post-install test scripts -test_script: - - node --version - - npm --version - - node test/test.js - -# Don't actually build -build: off diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index ee1eb74ed8..b43cac375e 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -76,14 +76,18 @@ pipeline { script { def node = readYaml(file: '.ci/.jenkins_nodejs.yml') def parallelTasks = [:] - def parallelTasksWithoutAsyncHooks = [:] node['NODEJS_VERSION'].each{ version -> parallelTasks["Node.js-${version}"] = generateStep(version: version) if (!version.startsWith('6')) { parallelTasks["Node.js-${version}-async-hooks-false"] = generateStep(version: version, disableAsyncHooks: true) } + // TODO: to be enabled if required. + // parallelTasks["Windows-Node.js-${version}"] = generateStepForWindows(version: version) } + // Only 12 for the time being + parallelTasks["Windows-Node.js-12"] = generateStepForWindows(version: '12') + // PRs don't require to run here as it's now managed within the linting pipeline if (!env.CHANGE_ID) { // Linting in parallel with the test stage @@ -330,28 +334,26 @@ def generateStep(Map params = [:]){ def edge = params.containsKey('edge') ? params.edge : false def disableAsyncHooks = params.get('disableAsyncHooks', false) return { - node('docker && linux && immutable'){ - try { - env.HOME = "${WORKSPACE}" - if (disableAsyncHooks) { - env.ELASTIC_APM_ASYNC_HOOKS = 'false' - } - deleteDir() - unstash 'source' - dir("${BASE_DIR}"){ - retry(2){ - sleep randomNumber(min:10, max: 30) - sh(label: "Run Tests", script: """.ci/scripts/test.sh "${version}" "${tav}" "${edge}" """) + node('linux && immutable'){ + withEnv(["VERSION=${version}", "ELASTIC_APM_ASYNC_HOOKS=${disableAsyncHooks}"]) { + try { + deleteDir() + unstash 'source' + dir("${BASE_DIR}"){ + retry(2){ + sleep randomNumber(min:5, max: 10) + sh(label: "Run Tests", script: """.ci/scripts/test.sh "${version}" "${tav}" "${edge}" """) + } } + } catch(e){ + error(e.toString()) + } finally { + docker.image('node:12').inside("-v ${WORKSPACE}/${BASE_DIR}:/app"){ + sh(label: "Convert Test results to JUnit format", script: 'cd /app && .ci/scripts/convert_tap_to_junit.sh') + } + junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/**/junit-*.xml") + codecov(repo: env.REPO, basedir: "${BASE_DIR}", secret: "${CODECOV_SECRET}") } - } catch(e){ - error(e.toString()) - } finally { - docker.image('node:12').inside("-v ${WORKSPACE}/${BASE_DIR}:/app"){ - sh(label: "Convert Test results to JUnit format", script: 'cd /app && .ci/scripts/convert_tap_to_junit.sh') - } - junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/**/junit-*.xml") - codecov(repo: env.REPO, basedir: "${BASE_DIR}", secret: "${CODECOV_SECRET}") } } } @@ -423,3 +425,47 @@ def getSmartTAVContext() { } } } + +def generateStepForWindows(Map params = [:]){ + def version = params?.version + def disableAsyncHooks = params.get('disableAsyncHooks', false) + return { + sh label: 'Prepare services', script: ".ci/scripts/windows/prepare-test.sh ${version}" + def linuxIp = sh(label: 'Get IP', script: '''hostname -I | awk '{print $1}' ''', returnStdout: true)?.trim() + node('windows-2019-docker-immutable'){ + // When installing with choco the PATH might not be updated within the already connected worker. + withEnv(["PATH=${PATH};C:\\Program Files\\nodejs", + "VERSION=${version}", + "ELASTIC_APM_ASYNC_HOOKS=${disableAsyncHooks}", + "CASSANDRA_HOST=${linuxIp}", + "ES_HOST=${linuxIp}", + "MEMCACHED_HOST=${linuxIp}", + "MONGODB_HOST=${linuxIp}", + "MSSQL_HOST=${linuxIp}", + "MYSQL_HOST=${linuxIp}", + "PGHOST=${linuxIp}", + "REDIS_HOST=${linuxIp}"]) { + try { + deleteDir() + unstash 'source' + dir(BASE_DIR) { + installTools([ [tool: 'nodejs', version: "${version}" ] ]) + bat label: 'Tool versions', script: ''' + npm --version + node --version + ''' + bat 'npm install' + bat 'node test/test.js' + } + } catch(e){ + error(e.toString()) + } finally { + echo 'JUnit archiving no yet in place' + } + } + } + + // If the above execution failed, then it will not reach this section. TBD + sh label: 'Stop services', script: ".ci/scripts/windows/stop-test.sh ${version}" + } +} diff --git a/.ci/scripts/windows/prepare-test.sh b/.ci/scripts/windows/prepare-test.sh new file mode 100755 index 0000000000..7f3513c099 --- /dev/null +++ b/.ci/scripts/windows/prepare-test.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -exo pipefail + +NODE_VERSION=${1:?Nodejs version missing NODE_VERSION is not set} + +NODE_VERSION=${NODE_VERSION} \ +USER_ID="$(id -u):$(id -g)" \ +docker-compose \ + --no-ansi \ + --log-level ERROR \ + -f .ci/docker/docker-compose-all.yml \ + up \ + --build \ + --remove-orphans \ + --quiet-pull \ + --detach diff --git a/.ci/scripts/windows/stop-test.sh b/.ci/scripts/windows/stop-test.sh new file mode 100755 index 0000000000..0b0a065304 --- /dev/null +++ b/.ci/scripts/windows/stop-test.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -exo pipefail + +NODE_VERSION=${1:?Nodejs version missing NODE_VERSION is not set} + +NODE_VERSION=${NODE_VERSION} docker-compose \ + --no-ansi \ + -f .ci/docker/docker-compose-all.yml \ + logs \ + --timestamps > docker-compose-logs.txt + +NODE_VERSION=${NODE_VERSION} docker-compose \ + --no-ansi \ + --log-level ERROR \ + -f .ci/docker/docker-compose-all.yml \ + down -v diff --git a/.gitignore b/.gitignore index bf09eabe3e..0d29481732 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ build coverage node_modules test/benchmarks/.tmp +coverage.lcov +test-suite-output.tap diff --git a/test/agent.js b/test/agent.js index 0971d7f13d..6858a3cda7 100644 --- a/test/agent.js +++ b/test/agent.js @@ -1362,10 +1362,7 @@ function assertMetadata (t, payload) { t.equal(payload.process.pid, process.pid) t.ok(payload.process.pid > 0, 'should have a pid greater than 0') t.ok(payload.process.title, 'should have a process title') - t.ok( - /(npm|node)/.test(payload.process.title), - `process.title should contain expected value (was: "${payload.process.title}")` - ) + t.equal(payload.process.title, process.title) t.deepEqual(payload.process.argv, process.argv) t.ok(payload.process.argv.length >= 2, 'should have at least two process arguments') } diff --git a/test/config.js b/test/config.js index 26783f200f..c9f5ae1bfa 100644 --- a/test/config.js +++ b/test/config.js @@ -451,13 +451,22 @@ test('serviceName defaults to package name', function (t) { { action: 'mkdirp', dir: path.join(tmp, 'node_modules') - }, - { + } + ] + + if (process.platform === 'win32') { + files.push({ + action: 'npm link', + from: path.resolve(__dirname, '..'), + to: tmp + }) + } else { + files.push({ action: 'symlink', from: path.resolve(__dirname, '..'), to: path.join(tmp, 'node_modules/elastic-apm-node') - } - ] + }) + } // NOTE: Reduce the sequence to a promise chain rather // than using Promise.all(), as the tasks are dependent. @@ -473,6 +482,15 @@ test('serviceName defaults to package name', function (t) { case 'symlink': { return symlink(file.from, file.to) } + case 'npm link': { + return exec('npm link', { + cwd: file.from + }).then(() => { + return exec('npm link elastic-apm-node', { + cwd: file.to + }) + }) + } } }) }, Promise.resolve()) diff --git a/test/instrumentation/modules/bluebird/bluebird.js b/test/instrumentation/modules/bluebird/bluebird.js index eee89c9b63..77dd5dbfab 100644 --- a/test/instrumentation/modules/bluebird/bluebird.js +++ b/test/instrumentation/modules/bluebird/bluebird.js @@ -927,7 +927,7 @@ test('Promise.promisify', function (t) { readFile(__filename, 'utf8').then(function (contents) { var firstLine = contents.split('\n')[0] - t.equal(firstLine, '\'use strict\'') + t.ok(/use strict/.test(firstLine)) t.equal(ins.currentTransaction.id, trans.id) }) }) diff --git a/test/instrumentation/modules/pg/_utils.js b/test/instrumentation/modules/pg/_utils.js index a675157599..1bef25531f 100644 --- a/test/instrumentation/modules/pg/_utils.js +++ b/test/instrumentation/modules/pg/_utils.js @@ -6,7 +6,10 @@ exports.reset = reset exports.loadData = loadData function reset (cb) { - var client = new Client({ database: 'postgres' }) + var client = new Client({ + database: 'postgres', + user: process.env.PGUSER || 'postgres' + }) client.connect(function (err) { if (err) throw err @@ -22,7 +25,10 @@ function reset (cb) { } function loadData (cb) { - var client = new Client({ database: 'test_elastic_apm' }) + var client = new Client({ + database: 'test_elastic_apm', + user: process.env.PGUSER || 'postgres' + }) client.connect(function (err) { if (err) throw err diff --git a/test/instrumentation/modules/pg/knex.js b/test/instrumentation/modules/pg/knex.js index 8bc9b66ba5..3c997cb5ca 100644 --- a/test/instrumentation/modules/pg/knex.js +++ b/test/instrumentation/modules/pg/knex.js @@ -138,7 +138,10 @@ function createClient (cb) { setup(function () { knex = Knex({ client: 'pg', - connection: 'postgres:///test_elastic_apm' + connection: { + database: 'test_elastic_apm', + user: process.env.PGUSER || 'postgres' + } }) cb() }) diff --git a/test/instrumentation/modules/pg/pg.js b/test/instrumentation/modules/pg/pg.js index d5bf0d9cc0..1374dd1b41 100644 --- a/test/instrumentation/modules/pg/pg.js +++ b/test/instrumentation/modules/pg/pg.js @@ -507,7 +507,8 @@ function assertBasicQuery (t, sql, data) { function createClient (cb) { setup(function () { queryable = new pg.Client({ - database: 'test_elastic_apm' + database: 'test_elastic_apm', + user: process.env.PGUSER || 'postgres' }) queryable.connect(function (err) { if (err) throw err @@ -523,11 +524,15 @@ function createPool (cb) { if (semver.satisfies(pgVersion, '<6.0.0')) { queryable = pg connector = function connector (cb) { - return pg.connect('postgres:///test_elastic_apm', cb) + return pg.connect({ + database: 'test_elastic_apm', + user: process.env.PGUSER || 'postgres' + }, cb) } } else { var pool = new pg.Pool({ - database: 'test_elastic_apm' + database: 'test_elastic_apm', + user: process.env.PGUSER || 'postgres' }) queryable = pool connector = function connector (cb) { diff --git a/test/script/appveyor/install-cassandra.ps1 b/test/script/appveyor/install-cassandra.ps1 deleted file mode 100755 index 6f5b9e9701..0000000000 --- a/test/script/appveyor/install-cassandra.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -# Abort with non zero exit code on errors -$ErrorActionPreference = "Stop" - -$cassandraVersion = "3.11.3" -$downloadUrl = "http://archive.apache.org/dist/cassandra/$cassandraVersion/apache-cassandra-$cassandraVersion-bin.tar.gz" -$extractRoot = "$env:USERPROFILE" -$tgzPath = "$extractRoot\cassandra.tar.gz" -$tarPath = "$extractRoot\cassandra.tar" -$cassandra = "$extractRoot\apache-cassandra-$cassandraVersion\bin\cassandra.bat" - -Write-Host "Downloading Cassandra..." -ForegroundColor Cyan -appveyor DownloadFile $downloadUrl -FileName $tgzPath - -Write-Host "Extracting Cassandra..." -7z e $tgzPath -tgzip -y -o"$extractRoot" | Out-Null -7z x $tarPath -ttar -r -aou -o"$extractRoot" | Out-Null - -Write-Host "Starting Cassandra..." -Start-Process $cassandra -PassThru - -Write-Host "Cassandra $cassandraVersion running" -ForegroundColor Green diff --git a/test/script/appveyor/install-elasticsearch.ps1 b/test/script/appveyor/install-elasticsearch.ps1 deleted file mode 100755 index d4da2cfbc8..0000000000 --- a/test/script/appveyor/install-elasticsearch.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -# Abort with non zero exit code on errors -$ErrorActionPreference = "Stop" - -Write-Host "Preparing to download and install Elasticsearch..." -ForegroundColor Cyan -$esVersion = "6.1.2" -$downloadUrl = "https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-$($esVersion).zip" -$zipPath = "$($env:USERPROFILE)\elasticsearch-$esVersion.zip" -$extractRoot = "$env:SYSTEMDRIVE\Elasticsearch" -$esRoot = "$extractRoot\elasticsearch-$esVersion" - -Write-Host "Downloading Elasticsearch..." -(New-Object Net.WebClient).DownloadFile($downloadUrl, $zipPath) -7z x $zipPath -y -o"$extractRoot" | Out-Null -del $zipPath - -Write-Host "Installing Elasticsearch as a Windows service..." -& "$esRoot\bin\elasticsearch-service.bat" install - -Write-Host "Starting Elasticsearch service..." -& "$esRoot\bin\elasticsearch-service.bat" start - -do { - Write-Host "Waiting for Elasticsearch service to bootstrap..." - sleep 1 -} until(Test-NetConnection localhost -Port 9200 | ? { $_.TcpTestSucceeded } ) - -Write-Host "Elasticsearch installed" -ForegroundColor Green diff --git a/test/script/appveyor/install-redis.ps1 b/test/script/appveyor/install-redis.ps1 deleted file mode 100755 index c8506ff4b0..0000000000 --- a/test/script/appveyor/install-redis.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -# Abort with non zero exit code on errors -$ErrorActionPreference = "Stop" - -Write-Host "Downloading Redis..." -ForegroundColor Cyan -$redisRoot = "$env:SYSTEMDRIVE\Redis" -$zipPath = "$($env:USERPROFILE)\redis-2.8.19.zip" -(New-Object Net.WebClient).DownloadFile('https://github.com/MSOpenTech/redis/releases/download/win-2.8.19/redis-2.8.19.zip', $zipPath) -7z x $zipPath -y -o"$redisRoot" | Out-Null -del $zipPath - -Write-Host "Installing Redis as a Windows service..." -& "$redisRoot\redis-server.exe" --service-install - -Write-Host "Starting Redis service..." -& "$redisRoot\redis-server.exe" --service-start - -Write-Host "Redis installed" -ForegroundColor Green