overleaf/libraries/metrics/test/acceptance/metrics_tests.js
Jakob Ackermann 2d1c84ea2e [misc] fix collection of open sockets
```
> require('http').globalAgent.sockets
{ 'HOST:PORT:': [{ ... }, { ... }] }
```

- `new URL('http://HOST:PORT:')` throws
  -> test suite `open_sockets`
- gauges were not cleaned up after no agents were present for a given
   key. This lead to continuous emission of the previous connection
   count for a given hostname, following the completion of all requests.
  -> test suite `when all requests complete`
2020-11-06 15:16:14 +00:00

245 lines
7 KiB
JavaScript

const os = require('os')
const http = require('http')
const { expect } = require('chai')
const Metrics = require('../..')
const HOSTNAME = os.hostname()
const APP_NAME = 'test-app'
describe('Metrics module', function() {
before(function() {
Metrics.initialize(APP_NAME)
})
describe('at startup', function() {
it('increments the process_startup counter', async function() {
await expectMetricValue('process_startup', 1)
})
it('collects default metrics', async function() {
const metric = await getMetric('process_cpu_user_seconds_total')
expect(metric).to.exist
})
})
describe('inc()', function() {
it('increments counts by 1', async function() {
Metrics.inc('duck_count')
await expectMetricValue('duck_count', 1)
Metrics.inc('duck_count')
Metrics.inc('duck_count')
await expectMetricValue('duck_count', 3)
})
it('escapes special characters in the key', async function() {
Metrics.inc('show.me the $!!')
await expectMetricValue('show_me_the____', 1)
})
})
describe('count()', function() {
it('increments counts by the given count', async function() {
Metrics.count('rabbit_count', 5)
await expectMetricValue('rabbit_count', 5)
Metrics.count('rabbit_count', 6)
Metrics.count('rabbit_count', 7)
await expectMetricValue('rabbit_count', 18)
})
})
describe('summary()', function() {
it('collects observations', async function() {
Metrics.summary('oven_temp', 200)
Metrics.summary('oven_temp', 300)
Metrics.summary('oven_temp', 450)
const sum = await getSummarySum('oven_temp')
expect(sum).to.equal(950)
})
})
describe('timing()', function() {
it('collects timings', async function() {
Metrics.timing('sprint_100m', 10)
Metrics.timing('sprint_100m', 20)
Metrics.timing('sprint_100m', 30)
const sum = await getSummarySum('timer_sprint_100m')
expect(sum).to.equal(60)
})
})
describe('gauge()', function() {
it('records values', async function() {
Metrics.gauge('water_level', 1.5)
await expectMetricValue('water_level', 1.5)
Metrics.gauge('water_level', 4.2)
await expectMetricValue('water_level', 4.2)
})
})
describe('globalGauge()', function() {
it('records values without a host label', async function() {
Metrics.globalGauge('tire_pressure', 99.99)
const { value, labels } = await getMetricValue('tire_pressure')
expect(value).to.equal(99.99)
expect(labels.host).to.equal('global')
expect(labels.app).to.equal(APP_NAME)
})
})
describe('open_sockets', function() {
const keyServer1 = 'open_connections_http_127_42_42_1'
const keyServer2 = 'open_connections_http_127_42_42_2'
let finish1, finish2, emitResponse1, emitResponse2
function resetEmitResponse1() {
emitResponse1 = new Promise(resolve => (finish1 = resolve))
}
resetEmitResponse1()
function resetEmitResponse2() {
emitResponse2 = new Promise(resolve => (finish2 = resolve))
}
resetEmitResponse2()
let server1, server2
before(function setupServer1(done) {
server1 = http.createServer((req, res) => {
res.write('...')
emitResponse1.then(() => res.end())
})
server1.listen(0, '127.42.42.1', done)
})
before(function setupServer2(done) {
server2 = http.createServer((req, res) => {
res.write('...')
emitResponse2.then(() => res.end())
})
server2.listen(0, '127.42.42.2', done)
})
after(function cleanupPendingRequests() {
finish1()
finish2()
})
after(function shutdownServer1(done) {
if (server1) server1.close(done)
})
after(function shutdownServer2(done) {
if (server2) server2.close(done)
})
let urlServer1, urlServer2
before(function setUrls() {
urlServer1 = `http://127.42.42.1:${server1.address().port}/`
urlServer2 = `http://127.42.42.2:${server2.address().port}/`
})
describe('gaugeOpenSockets()', function() {
beforeEach(function runGaugeOpenSockets() {
Metrics.open_sockets.gaugeOpenSockets()
})
describe('without pending connections', function() {
it('emits no open_connections', async function() {
await expectNoMetricValue(keyServer1)
await expectNoMetricValue(keyServer2)
})
})
describe('with pending connections for server1', function() {
before(function(done) {
http.get(urlServer1)
http.get(urlServer1)
setTimeout(done, 10)
})
it('emits 2 open_connections for server1', async function() {
await expectMetricValue(keyServer1, 2)
})
it('emits no open_connections for server2', async function() {
await expectNoMetricValue(keyServer2)
})
})
describe('with pending connections for server1 and server2', function() {
before(function(done) {
http.get(urlServer2)
http.get(urlServer2)
setTimeout(done, 10)
})
it('emits 2 open_connections for server1', async function() {
await expectMetricValue(keyServer1, 2)
})
it('emits 2 open_connections for server1', async function() {
await expectMetricValue(keyServer1, 2)
})
})
describe('when requests finish for server1', function() {
before(function(done) {
finish1()
resetEmitResponse1()
http.get(urlServer1)
setTimeout(done, 10)
})
it('emits 1 open_connections for server1', async function() {
await expectMetricValue(keyServer1, 1)
})
it('emits 2 open_connections for server2', async function() {
await expectMetricValue(keyServer2, 2)
})
})
describe('when all requests complete', function() {
before(function(done) {
finish1()
finish2()
setTimeout(done, 10)
})
it('emits no open_connections', async function() {
await expectNoMetricValue(keyServer1)
await expectNoMetricValue(keyServer2)
})
})
})
})
})
function getMetric(key) {
return Metrics.register.getSingleMetric(key)
}
async function getSummarySum(key) {
const metric = getMetric(key)
const item = await metric.get()
for (const value of item.values) {
if (value.metricName === `${key}_sum`) {
return value.value
}
}
return null
}
async function getMetricValue(key) {
const metrics = await Metrics.register.getMetricsAsJSON()
const metric = metrics.find(m => m.name === key)
return metric.values[0]
}
async function expectMetricValue(key, expectedValue) {
const value = await getMetricValue(key)
expect(value.value).to.equal(expectedValue)
expect(value.labels.host).to.equal(HOSTNAME)
expect(value.labels.app).to.equal(APP_NAME)
}
async function expectNoMetricValue(key) {
const metric = getMetric(key)
if (!metric) return
await expectMetricValue(key, 0)
}