first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const chai = require('chai')
const { expect } = chai
const path = require('node:path')
const modulePath = path.join(__dirname, '../../../event_loop.js')
const SandboxedModule = require('sandboxed-module')
const sinon = require('sinon')
describe('event_loop', function () {
before(function () {
this.metrics = {
timing: sinon.stub(),
registerDestructor: sinon.stub(),
}
this.logger = {
warn: sinon.stub(),
}
return (this.event_loop = SandboxedModule.require(modulePath, {
requires: {
'./index': this.metrics,
},
}))
})
describe('with a logger provided', function () {
before(function () {
return this.event_loop.monitor(this.logger)
})
return it('should register a destructor with metrics', function () {
return expect(this.metrics.registerDestructor.called).to.equal(true)
})
})
return describe('without a logger provided', function () {
return it('should throw an exception', function () {
return expect(this.event_loop.monitor).to.throw('logger is undefined')
})
})
})

View File

@@ -0,0 +1,171 @@
const Path = require('node:path')
const SandboxedModule = require('sandboxed-module')
const sinon = require('sinon')
const MODULE_PATH = Path.join(__dirname, '../../../http.js')
describe('http.monitor', function () {
beforeEach(function () {
this.req = {
method: 'POST',
url: '/project/1234/cleanup',
headers: {
'content-length': '123',
},
route: {
path: '/project/:id/cleanup',
},
}
this.originalResponseEnd = sinon.stub()
this.res = {
end: this.originalResponseEnd,
}
this.data = 'data'
this.logger = {
debug: sinon.stub(),
info: sinon.stub(),
warn: sinon.stub(),
}
this.Metrics = {
timing: sinon.stub(),
summary: sinon.stub(),
}
this.clock = sinon.useFakeTimers()
this.http = SandboxedModule.require(MODULE_PATH, {
requires: {
'./index': this.Metrics,
},
})
})
afterEach(function () {
this.clock.restore()
})
describe('with the default options', function () {
beforeEach('set up the monitor', function (done) {
this.http.monitor(this.logger)(this.req, this.res, done)
})
describe('after a simple request', function () {
endRequest()
expectOriginalEndCalled()
expectMetrics()
it('logs the request at the DEBUG level', function () {
sinon.assert.calledWith(
this.logger.debug,
{ req: this.req, res: this.res, responseTimeMs: 500 },
'%s %s',
this.req.method,
this.req.url
)
})
})
describe('when logging is disabled', function () {
beforeEach('disable logging', function () {
this.req.logger.disable()
})
endRequest()
expectOriginalEndCalled()
expectMetrics()
it("doesn't log the request", function () {
sinon.assert.notCalled(this.logger.debug)
})
})
describe('with custom log fields', function () {
beforeEach('add custom fields', function () {
this.req.logger.addFields({ a: 1, b: 2 })
})
endRequest()
it('logs the request with the custom log fields', function () {
sinon.assert.calledWith(
this.logger.debug,
{ req: this.req, res: this.res, responseTimeMs: 500, a: 1, b: 2 },
'%s %s',
this.req.method,
this.req.url
)
})
})
describe('when setting the log level', function () {
beforeEach('set custom level', function () {
this.req.logger.setLevel('warn')
})
endRequest()
it('logs the request at the custom level', function () {
sinon.assert.calledWith(
this.logger.warn,
{ req: this.req, res: this.res, responseTimeMs: 500 },
'%s %s',
this.req.method,
this.req.url
)
})
})
})
describe('with a different default log level', function () {
beforeEach('set up the monitor', function (done) {
this.http.monitor(this.logger, 'info')(this.req, this.res, done)
})
endRequest()
it('logs the request at that level', function () {
sinon.assert.calledWith(
this.logger.info,
{ req: this.req, res: this.res, responseTimeMs: 500 },
'%s %s',
this.req.method,
this.req.url
)
})
})
})
function endRequest() {
beforeEach('end the request', function () {
this.clock.tick(500)
this.res.end(this.data)
})
}
function expectOriginalEndCalled() {
it('calls the original res.end()', function () {
sinon.assert.calledWith(this.originalResponseEnd, this.data)
})
}
function expectMetrics() {
it('records the response time', function () {
sinon.assert.calledWith(this.Metrics.timing, 'http_request', 500, null, {
method: this.req.method,
status_code: this.res.status_code,
path: 'project_id_cleanup',
})
})
it('records the request size', function () {
sinon.assert.calledWith(
this.Metrics.summary,
'http_request_size_bytes',
123,
{
method: this.req.method,
status_code: this.res.status_code,
path: 'project_id_cleanup',
}
)
})
}

View File

@@ -0,0 +1,89 @@
const Metrics = require('../../..')
const { expect } = require('chai')
const prom = require('prom-client')
describe('mongodb', function () {
beforeEach(function () {
prom.register.clear()
this.pool = {
totalConnectionCount: 8,
availableConnectionCount: 2,
waitQueueSize: 4,
options: { maxPoolSize: 10 },
}
this.servers = new Map([['server1', { s: { pool: this.pool } }]])
this.mongoClient = { topology: { s: { servers: this.servers } } }
})
it('handles an unconnected client', async function () {
const mongoClient = {}
Metrics.mongodb.monitor(mongoClient)
const metrics = await getMetrics()
expect(metrics).to.deep.equal({})
})
it('collects Mongo metrics', async function () {
Metrics.mongodb.monitor(this.mongoClient)
const metrics = await getMetrics()
expect(metrics).to.deep.equal({
'mongo_connection_pool_max:server1': 10,
'mongo_connection_pool_size:server1': 8,
'mongo_connection_pool_available:server1': 2,
'mongo_connection_pool_waiting:server1': 4,
})
})
it('handles topology changes', async function () {
Metrics.mongodb.monitor(this.mongoClient)
let metrics = await getMetrics()
expect(metrics).to.deep.equal({
'mongo_connection_pool_max:server1': 10,
'mongo_connection_pool_size:server1': 8,
'mongo_connection_pool_available:server1': 2,
'mongo_connection_pool_waiting:server1': 4,
})
// Add a server
this.servers.set('server2', this.servers.get('server1'))
metrics = await getMetrics()
expect(metrics).to.deep.equal({
'mongo_connection_pool_max:server1': 10,
'mongo_connection_pool_size:server1': 8,
'mongo_connection_pool_available:server1': 2,
'mongo_connection_pool_waiting:server1': 4,
'mongo_connection_pool_max:server2': 10,
'mongo_connection_pool_size:server2': 8,
'mongo_connection_pool_available:server2': 2,
'mongo_connection_pool_waiting:server2': 4,
})
// Delete a server
this.servers.delete('server1')
metrics = await getMetrics()
expect(metrics).to.deep.equal({
'mongo_connection_pool_max:server2': 10,
'mongo_connection_pool_size:server2': 8,
'mongo_connection_pool_available:server2': 2,
'mongo_connection_pool_waiting:server2': 4,
})
// Delete another server
this.servers.delete('server2')
metrics = await getMetrics()
expect(metrics).to.deep.equal({})
})
})
async function getMetrics() {
const metrics = await prom.register.getMetricsAsJSON()
const result = {}
for (const metric of metrics) {
for (const value of metric.values) {
const key = `${metric.name}:${value.labels.mongo_server}`
result[key] = value.value
}
}
return result
}