From b14048c512937e5774f179ef51c68ce3f2505e85 Mon Sep 17 00:00:00 2001 From: Pablo Ubal Naveira Date: Mon, 11 Nov 2024 12:21:01 +0100 Subject: [PATCH] bumped minor version --- .github/workflows/ci.yml | 2 ++ package.json | 3 +- test/db.tests.js | 68 +++++++++++++++------------------------- test/testutils.js | 54 +++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 test/testutils.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16da035..c2e71b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,8 @@ on: jobs: test: + env: + CI: true timeout-minutes: 10 runs-on: ubuntu-latest strategy: diff --git a/package.json b/package.json index 8ede626..2f09ae5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "limitd-redis", - "version": "8.3.1", + "version": "8.4.0", "description": "A database client for limits on top of redis", "main": "index.js", "repository": { @@ -30,6 +30,7 @@ "mockdate": "^3.0.5", "nyc": "^14.1.1", "sinon": "^19.0.2", + "sockopt": "^2.0.1", "toxiproxy-node-client": "^2.0.6" } } diff --git a/test/db.tests.js b/test/db.tests.js index 228de83..1bd5fa4 100644 --- a/test/db.tests.js +++ b/test/db.tests.js @@ -6,6 +6,7 @@ const assert = require('chai').assert; const { endOfMonthTimestamp, replicateHashtag } = require('../lib/utils'); const sinon = require('sinon'); const { exec } = require('child_process'); +const { getSockOptValue } = require('./testutils'); const buckets = { ip: { @@ -145,48 +146,28 @@ module.exports.tests = (clientCreator, opts) => { }); describe('KeepAlive', () => { - const checkSocketOption = (portPairs, option, expectedValue, delta, done) => { - const pid = process.pid; - const command = `lsof -a -p ${pid} -i 4 -T f`; - - exec(command, (error, stdout, stderr) => { - if (error) { - return done(error); - } - if (stderr) { - return done(new Error(stderr)); - } - - const keepAliveOption = stdout - .split('\n') - .find(line => - portPairs.some(portPair => - line.includes(`:${portPair.localPort}`) - && line.includes(`:${portPair.remotePort}`) - ) - ); - assert.isNotNull(keepAliveOption, `no entry found for port ${portPairs}: ${stdout}`); - assert.notEqual(keepAliveOption, undefined, `no entry found for port ${portPairs}: ${stdout}`); - assert.include(keepAliveOption, option, `${option} option not found: ${keepAliveOption}`); - - const keepAliveValue = parseInt(keepAliveOption.match(new RegExp(`${option}=(\\d+)`))[1], 10); - assert.isAtLeast(keepAliveValue, expectedValue - delta, `${option} is lesser than expected`); - assert.isAtMost(keepAliveValue, expectedValue + delta, `${option} is greater than expected`); - - done(); - }); + const assertSockOpt = (expected, delta, done) => (err, value) => { + if (err) { + done(err); + } + assert.isAtLeast(value, expected - delta, 'SO=KEEPALIVE is lesser than expected'); + assert.isAtMost(value, expected + delta, 'SO=KEEPALIVE is greater than expected'); + done(); }; + it('should set SO=KEEPALIVE to 10000 by default', (done) => { - const ports = []; + let socket; if (opts.clusterNodes) { - Object.values(db.redis.connectionPool.nodes.all).forEach(node => { - ports.push({ localPort: node.stream.localPort, remotePort: node.stream.remotePort }); - }); + const node = Object.values(db.redis.connectionPool.nodes.all).find(node => node.status === 'ready'); + if (!node) { + return done(new Error('No ready node found')); + } + socket = node.stream; } else { - ports.push({ localPort: db.redis.stream.localPort, remotePort: db.redis.stream.remotePort }); + socket = db.redis.stream; } - checkSocketOption(ports, 'SO=KEEPALIVE', 10000, 10, done); + getSockOptValue(socket, 'SO=KEEPALIVE', assertSockOpt(10000, 10, done) ); }); describe('when setting keepAlive option', () => { @@ -200,16 +181,17 @@ module.exports.tests = (clientCreator, opts) => { it('should set SO=KEEPALIVE to the value specified in the constructor config', (done) => { const ports = []; + let socket; if (opts.clusterNodes) { - Object.values(db.redis.connectionPool.nodes.all) - .filter(node => node.status === 'ready') - .forEach(node => { - ports.push({ localPort: node.stream.localPort, remotePort: node.stream.remotePort }); - }); + const node = Object.values(db.redis.connectionPool.nodes.all).find(node => node.status === 'ready'); + if (!node) { + return done(new Error('No ready node found')); + } + socket = node.stream; } else { - ports.push({ localPort: db.redis.stream.localPort, remotePort: db.redis.stream.remotePort }); + socket = db.redis.stream; } - checkSocketOption(ports, 'SO=KEEPALIVE', keepAliveValue, 10, done); + getSockOptValue(socket, 'SO=KEEPALIVE', assertSockOpt(5000, 10, done) ); }); }); }); diff --git a/test/testutils.js b/test/testutils.js new file mode 100644 index 0000000..af0b3c6 --- /dev/null +++ b/test/testutils.js @@ -0,0 +1,54 @@ +const { exec } = require('child_process'); +const { getsockopt } = require('sockopt'); + +const getSockOptValue = (socket, opt, cb) => { + if (process.env.CI !== undefined) { + return linuxGetSockOptValue(socket, opt, cb); + } + + return macosGetSockOptValue(socket, opt, cb); +}; + +const macosGetSockOptValue = (socket, opt, cb) => { + const pid = process.pid; + exec(`lsof -a -p ${pid} -i 4 -T f`, (error, stdout, stderr) => { + if (error) { + return cb(error); + } + if (stderr) { + return cb(new Error(stderr)); + } + + const keepAliveOption = stdout + .split('\n') + .find(line => line.includes(`:${socket.localPort}`) && line.includes(`:${socket.remotePort}`)); + + if (!keepAliveOption) { + cb(new Error(`no entry found for local port ${socket.localPort}, and remote port ${socket.remotePort}`)); + } + + if (!keepAliveOption.includes(opt)) { + cb(new Error(`${opt} option not found: ${keepAliveOption}`)); + } + + const keepAliveValue = parseInt(keepAliveOption.match(new RegExp(`${opt}=(\\d+)`))[1], 10); + cb(null, keepAliveValue); + }); +}; + +const linuxGetSockOptValue = (socket, opt, cb) => { + const SOL_SOCKET = 0xffff; + const SOCK_OPTS = { + 'SO=KEEPALIVE': 0x0008, + }; + if (!SOCK_OPTS[opt]) { + return cb(new Error(`Unknown socket option: ${opt}`)); + } + + const keepAliveValue = getsockopt(socket, SOL_SOCKET, SOCK_OPTS[opt]); + cb(null, keepAliveValue); +}; + +module.exports = { + getSockOptValue +};