From 1091257ce38e1398006c601a1e2e82c5939cdb2e Mon Sep 17 00:00:00 2001 From: Bernardo Guerreiro <39738771+bernardobridge@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:29:19 +0000 Subject: [PATCH] test(plugins): add tests for metrics-by-endpoint plugin (#2385) * test(plugins): add unit tests for metrics-by-endpoint * test(plugins): add simple acceptance test for metrics-by-endpoint --- package-lock.json | 188 ++++---- .../package.json | 8 +- .../test/fixtures/scenario.yml | 14 + .../test/index.spec.js | 46 ++ .../test/index.unit.js | 448 ++++++++++++++++++ 5 files changed, 607 insertions(+), 97 deletions(-) create mode 100644 packages/artillery-plugin-metrics-by-endpoint/test/fixtures/scenario.yml create mode 100644 packages/artillery-plugin-metrics-by-endpoint/test/index.spec.js create mode 100644 packages/artillery-plugin-metrics-by-endpoint/test/index.unit.js diff --git a/package-lock.json b/package-lock.json index 1763eb8cf3..11fb9da405 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17810,9 +17810,9 @@ } }, "node_modules/tap": { - "version": "16.3.8", - "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.8.tgz", - "integrity": "sha512-ARpCLtOFST37MholnZm7JMFikGq0x/T9uBdZH83iuddPNgwDTZQiD8+4x7VABUfVWS0ozKUkmHZ5OOzMI3fLPg==", + "version": "16.3.10", + "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.10.tgz", + "integrity": "sha512-q5Am+PpGHS6JSjk/Zn4bCRBihmZVM15v/MYXUy60wenw5HDe7pVrevLCEoMEz7tuw6jaPOJJqni1y8apN23IGw==", "bundleDependencies": [ "ink", "treport", @@ -17956,19 +17956,20 @@ } }, "node_modules/tap/node_modules/@babel/code-frame": { - "version": "7.22.5", + "version": "7.23.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/tap/node_modules/@babel/compat-data": { - "version": "7.22.9", + "version": "7.23.5", "dev": true, "inBundle": true, "license": "MIT", @@ -17977,25 +17978,25 @@ } }, "node_modules/tap/node_modules/@babel/core": { - "version": "7.22.9", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.6", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -18007,12 +18008,12 @@ } }, "node_modules/tap/node_modules/@babel/generator": { - "version": "7.22.9", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -18034,26 +18035,23 @@ } }, "node_modules/tap/node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/tap/node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", + "version": "7.22.20", "dev": true, "inBundle": true, "license": "MIT", @@ -18062,13 +18060,13 @@ } }, "node_modules/tap/node_modules/@babel/helper-function-name": { - "version": "7.22.5", + "version": "7.23.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -18087,28 +18085,28 @@ } }, "node_modules/tap/node_modules/@babel/helper-module-imports": { - "version": "7.22.5", + "version": "7.22.15", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/tap/node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", + "version": "7.23.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -18151,7 +18149,7 @@ } }, "node_modules/tap/node_modules/@babel/helper-string-parser": { - "version": "7.22.5", + "version": "7.23.4", "dev": true, "inBundle": true, "license": "MIT", @@ -18160,7 +18158,7 @@ } }, "node_modules/tap/node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", + "version": "7.22.20", "dev": true, "inBundle": true, "license": "MIT", @@ -18169,7 +18167,7 @@ } }, "node_modules/tap/node_modules/@babel/helper-validator-option": { - "version": "7.22.5", + "version": "7.23.5", "dev": true, "inBundle": true, "license": "MIT", @@ -18178,27 +18176,27 @@ } }, "node_modules/tap/node_modules/@babel/helpers": { - "version": "7.22.6", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/tap/node_modules/@babel/highlight": { - "version": "7.22.5", + "version": "7.23.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -18206,7 +18204,7 @@ } }, "node_modules/tap/node_modules/@babel/parser": { - "version": "7.22.7", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", @@ -18237,7 +18235,7 @@ } }, "node_modules/tap/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", + "version": "7.23.3", "dev": true, "inBundle": true, "license": "MIT", @@ -18264,7 +18262,7 @@ } }, "node_modules/tap/node_modules/@babel/plugin-transform-destructuring": { - "version": "7.22.5", + "version": "7.23.3", "dev": true, "inBundle": true, "license": "MIT", @@ -18279,7 +18277,7 @@ } }, "node_modules/tap/node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.5", + "version": "7.23.3", "dev": true, "inBundle": true, "license": "MIT", @@ -18294,16 +18292,16 @@ } }, "node_modules/tap/node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.22.5", + "version": "7.23.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" }, "engines": { "node": ">=6.9.0" @@ -18313,34 +18311,34 @@ } }, "node_modules/tap/node_modules/@babel/template": { - "version": "7.22.5", + "version": "7.22.15", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/tap/node_modules/@babel/traverse": { - "version": "7.22.8", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -18348,13 +18346,13 @@ } }, "node_modules/tap/node_modules/@babel/types": { - "version": "7.22.5", + "version": "7.23.6", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -18396,7 +18394,7 @@ } }, "node_modules/tap/node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", + "version": "3.1.1", "dev": true, "inBundle": true, "license": "MIT", @@ -18420,29 +18418,23 @@ "license": "MIT" }, "node_modules/tap/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", + "version": "0.3.20", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/tap/node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true, - "inBundle": true, - "license": "MIT" - }, "node_modules/tap/node_modules/@types/prop-types": { - "version": "15.7.5", + "version": "15.7.11", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/tap/node_modules/@types/react": { - "version": "17.0.62", + "version": "17.0.73", "dev": true, "inBundle": true, "license": "MIT", @@ -18453,7 +18445,7 @@ } }, "node_modules/tap/node_modules/@types/scheduler": { - "version": "0.16.3", + "version": "0.16.8", "dev": true, "inBundle": true, "license": "MIT" @@ -18556,7 +18548,7 @@ } }, "node_modules/tap/node_modules/browserslist": { - "version": "4.21.9", + "version": "4.22.2", "dev": true, "funding": [ { @@ -18575,10 +18567,10 @@ "inBundle": true, "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -18621,7 +18613,7 @@ } }, "node_modules/tap/node_modules/caniuse-lite": { - "version": "1.0.30001517", + "version": "1.0.30001570", "dev": true, "funding": [ { @@ -18753,7 +18745,7 @@ "license": "MIT" }, "node_modules/tap/node_modules/convert-source-map": { - "version": "1.9.0", + "version": "2.0.0", "dev": true, "inBundle": true, "license": "MIT" @@ -18768,7 +18760,7 @@ } }, "node_modules/tap/node_modules/csstype": { - "version": "3.1.2", + "version": "3.1.3", "dev": true, "inBundle": true, "license": "MIT" @@ -18791,7 +18783,7 @@ } }, "node_modules/tap/node_modules/electron-to-chromium": { - "version": "1.4.477", + "version": "1.4.614", "dev": true, "inBundle": true, "license": "ISC" @@ -19222,7 +19214,7 @@ "license": "MIT" }, "node_modules/tap/node_modules/node-releases": { - "version": "2.0.13", + "version": "2.0.14", "dev": true, "inBundle": true, "license": "MIT" @@ -19342,7 +19334,7 @@ } }, "node_modules/tap/node_modules/punycode": { - "version": "2.3.0", + "version": "2.3.1", "dev": true, "inBundle": true, "license": "MIT", @@ -19364,7 +19356,7 @@ } }, "node_modules/tap/node_modules/react-devtools-core": { - "version": "4.28.0", + "version": "4.28.5", "dev": true, "inBundle": true, "license": "MIT", @@ -19736,7 +19728,7 @@ } }, "node_modules/tap/node_modules/update-browserslist-db": { - "version": "1.0.11", + "version": "1.0.13", "dev": true, "funding": [ { @@ -22356,6 +22348,10 @@ "license": "MPL-2.0", "dependencies": { "debug": "^4.3.2" + }, + "devDependencies": { + "tap": "^16.3.8", + "zx": "^4.3.0" } }, "packages/artillery-plugin-publish-metrics": { diff --git a/packages/artillery-plugin-metrics-by-endpoint/package.json b/packages/artillery-plugin-metrics-by-endpoint/package.json index 722a555092..21a4cf8e06 100644 --- a/packages/artillery-plugin-metrics-by-endpoint/package.json +++ b/packages/artillery-plugin-metrics-by-endpoint/package.json @@ -4,12 +4,18 @@ "description": "Per-endpoint breakdown of latency and response codes for Artillery HTTP tests.", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 0" + "test": "npm run test:unit && npm run test:acceptance", + "test:acceptance": "export ARTILLERY_TELEMETRY_DEFAULTS='{\"source\":\"test-suite\"}' && tap ./test/*.spec.js --timeout 300 --no-coverage --color", + "test:unit": "tap --no-coverage --color test/*.unit.js" }, "keywords": [], "author": "Hassy Veldstra ", "license": "MPL-2.0", "dependencies": { "debug": "^4.3.2" + }, + "devDependencies": { + "tap": "^16.3.8", + "zx": "^4.3.0" } } diff --git a/packages/artillery-plugin-metrics-by-endpoint/test/fixtures/scenario.yml b/packages/artillery-plugin-metrics-by-endpoint/test/fixtures/scenario.yml new file mode 100644 index 0000000000..22e1bb815b --- /dev/null +++ b/packages/artillery-plugin-metrics-by-endpoint/test/fixtures/scenario.yml @@ -0,0 +1,14 @@ +config: + target: http://asciiart.artillery.io:8080 + phases: + - duration: 2 + arrivalRate: 2 + plugins: + metrics-by-endpoint: {} + +scenarios: + - flow: + - get: + url: "/dino" + - get: + url: "/pony" \ No newline at end of file diff --git a/packages/artillery-plugin-metrics-by-endpoint/test/index.spec.js b/packages/artillery-plugin-metrics-by-endpoint/test/index.spec.js new file mode 100644 index 0000000000..a3e4493a94 --- /dev/null +++ b/packages/artillery-plugin-metrics-by-endpoint/test/index.spec.js @@ -0,0 +1,46 @@ +const { test } = require('tap'); +const { $ } = require('zx'); +const os = require('os'); + +test('reports in console and json report correctly', async (t) => { + const reportPath = + os.tmpdir() + '/artillery-plugin-metrics-by-endpoint-test.json'; + const output = + await $`../artillery/bin/run run ./test/fixtures/scenario.yml -o ${reportPath}`; + + // Assert console output includes plugin metrics + t.ok(output.stdout.includes('plugins.metrics-by-endpoint./dino.codes.200:')); + t.ok(output.stdout.includes('plugins.metrics-by-endpoint./pony.codes.200:')); + t.ok( + output.stdout.includes('plugins.metrics-by-endpoint.response_time./dino') + ); + t.ok( + output.stdout.includes('plugins.metrics-by-endpoint.response_time./pony') + ); + + //Assert json report includes plugin metrics + const jsonReport = require(reportPath); + + t.equal( + jsonReport.aggregate.counters[ + 'plugins.metrics-by-endpoint./dino.codes.200' + ], + 4 + ); + t.equal( + jsonReport.aggregate.counters[ + 'plugins.metrics-by-endpoint./pony.codes.200' + ], + 4 + ); + t.ok( + Object.keys(jsonReport.aggregate.summaries).includes( + 'plugins.metrics-by-endpoint.response_time./dino' + ) + ); + t.ok( + Object.keys(jsonReport.aggregate.summaries).includes( + 'plugins.metrics-by-endpoint.response_time./pony' + ) + ); +}); diff --git a/packages/artillery-plugin-metrics-by-endpoint/test/index.unit.js b/packages/artillery-plugin-metrics-by-endpoint/test/index.unit.js new file mode 100644 index 0000000000..9494e71d74 --- /dev/null +++ b/packages/artillery-plugin-metrics-by-endpoint/test/index.unit.js @@ -0,0 +1,448 @@ +const { test, beforeEach } = require('tap'); +const { Plugin } = require('../index'); +const { EventEmitter } = require('events'); + +const baseScript = { + config: { + plugins: { + 'metrics-by-endpoint': {} + } + }, + scenarios: [ + { + flow: [ + { + get: { + url: '/dino' + }, + post: { + url: '/rabbit' + } + } + ] + }, + { + flow: [ + { + get: { + url: '/potato' + } + } + ] + } + ] +}; + +test('afterResponse', async (t) => { + let defaultPluginPrefix = 'plugins.metrics-by-endpoint'; + let script; + let hookArgs; + let results; + + beforeEach(() => { + script = baseScript; + global.artillery = { + version: '2.0.3' + }; + process.env.LOCAL_WORKER_ID = 'abc123'; + results = { + counters: [], + histograms: [], + calledDone: false + }; + + const eventStub = new EventEmitter(); + eventStub.on('counter', (name, value) => { + results.counters.push({ name, value }); + }); + + eventStub.on('histogram', (name, value) => { + results.histograms.push({ name, value }); + }); + + hookArgs = { + req: { + url: '/dino' + }, + res: { + statusCode: 203, + headers: {}, + timings: { + phases: { + firstByte: 107 + } + } + }, + userContext: { + vars: { + target: 'http://example.com' + } + }, + events: eventStub, + done: () => { + results.calledDone = true; + } + }; + }); + + t.test('sets up afterResponse hook correctly', async (t) => { + new Plugin(script, hookArgs.events); + + // check afterResponse is in processor + t.hasProp(script.config.processor, 'metricsByEndpoint_afterResponse'); + + // check afterResponse is each scenario + script.scenarios.forEach((scenario) => { + t.equal(scenario.afterResponse.length, 1); + t.equal(scenario.afterResponse[0], 'metricsByEndpoint_afterResponse'); + }); + }); + + t.test('only runs plugin inside workers', async (t) => { + delete process.env.LOCAL_WORKER_ID; + script.config.processor = {}; + new Plugin(script, hookArgs.events); + + t.equal(Object.keys(script.config.processor).length, 0); + }); + + t.test( + 'emits counter and histogram metrics correctly with basic configuration', + async (t) => { + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}./dino.codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time./dino` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + + t.equal(results.calledDone, true); + } + ); + + t.test( + 'uses request url hostname over target hostname if they differ', + async (t) => { + const requestUrlWithoutProtocol = 'www.artillery.io/docs'; + hookArgs.req.url = `http://${requestUrlWithoutProtocol}`; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}.${requestUrlWithoutProtocol}.codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time.${requestUrlWithoutProtocol}` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + + t.equal(results.calledDone, true); + } + ); + + t.test('uses request url port over target port if they differ', async (t) => { + const pathWithPort = ':8081/dino'; + const requestWithPort = `${hookArgs.userContext.vars.target}${pathWithPort}`; + hookArgs.req.url = requestWithPort; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}.${pathWithPort}.codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time.${pathWithPort}` + ); + t.equal(results.histograms[0].value, hookArgs.res.timings.phases.firstByte); + + t.equal(results.calledDone, true); + }); + + t.test('emits histogram metrics correctly with server-timing', async (t) => { + new Plugin(script, hookArgs.events); + + const serverTiming = 105; + hookArgs.res.headers['server-timing'] = `total;dur=${serverTiming}`; + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.server-timing./dino` + ); + t.equal(results.histograms[0].value, serverTiming); + t.equal( + results.histograms[1].name, + `${defaultPluginPrefix}.response_time./dino` + ); + t.equal(results.histograms[1].value, hookArgs.res.timings.phases.firstByte); + + t.equal(results.calledDone, true); + }); + + t.test( + 'sets server timing to -1 if server timing header does not match correctly', + async (t) => { + new Plugin(script, hookArgs.events); + + const serverTiming = 105; + hookArgs.res.headers['server-timing'] = `total;potatoes=${serverTiming}`; + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.server-timing./dino` + ); + t.equal(results.histograms[0].value, -1); + t.equal( + results.histograms[1].name, + `${defaultPluginPrefix}.response_time./dino` + ); + t.equal( + results.histograms[1].value, + hookArgs.res.timings.phases.firstByte + ); + + t.equal(results.calledDone, true); + } + ); + + t.test('includes req name in metric name if req.name is set', async (t) => { + new Plugin(script, hookArgs.events); + + const reqName = 'bunnyRequest123'; + hookArgs.req.name = reqName; + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}./dino (${reqName}).codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time./dino (${reqName})` + ); + t.equal(results.histograms[0].value, hookArgs.res.timings.phases.firstByte); + }); + + t.test( + 'uses req name if req.name and useOnlyRequestNames are set', + async (t) => { + script.config.plugins['metrics-by-endpoint'] = { + useOnlyRequestNames: true + }; + new Plugin(script, hookArgs.events); + + const reqName = 'bunnyRequest123'; + hookArgs.req.name = reqName; + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}.${reqName}.codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time.${reqName}` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + } + ); + + t.test( + 'overrides default prefix if metricsNamespace option is set', + async (t) => { + const metricsNamespace = 'my-metrics'; + script.config.plugins['metrics-by-endpoint'] = { + metricsNamespace + }; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal(results.counters[0].name, `${metricsNamespace}./dino.codes.203`); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${metricsNamespace}.response_time./dino` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + } + ); + + t.test( + 'no metrics are emitted if ignoreUnnamedRequests is set and no name is set', + async (t) => { + script.config.plugins['metrics-by-endpoint'] = { + ignoreUnnamedRequests: true + }; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal(results.counters.length, 0); + t.equal(results.histograms.length, 0); + } + ); + + t.test( + 'metrics are emitted if ignoreUnnamedRequests is set and name is set', + async (t) => { + script.config.plugins['metrics-by-endpoint'] = { + ignoreUnnamedRequests: true + }; + hookArgs.req.name = 'iAmNamed'; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}./dino (${hookArgs.req.name}).codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time./dino (${hookArgs.req.name})` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + } + ); + + t.test( + 'strips query string from url when stripQueryString is set', + async (t) => { + script.config.plugins['metrics-by-endpoint'] = { + stripQueryString: true + }; + hookArgs.req.url = '/dino?query=stringy&another=one'; + new Plugin(script, hookArgs.events); + + script.config.processor.metricsByEndpoint_afterResponse( + hookArgs.req, + hookArgs.res, + hookArgs.userContext, + hookArgs.events, + hookArgs.done + ); + + t.equal( + results.counters[0].name, + `${defaultPluginPrefix}./dino.codes.203` + ); + t.equal(results.counters[0].value, 1); + + t.equal( + results.histograms[0].name, + `${defaultPluginPrefix}.response_time./dino` + ); + t.equal( + results.histograms[0].value, + hookArgs.res.timings.phases.firstByte + ); + } + ); +});