From 6d58a755b4d692970d3f2066903a4d503f334f0a Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Thu, 6 Oct 2022 07:46:56 +0200 Subject: [PATCH] fix: properly track events with acknowledgement "event_received" events did include the acknowledgement function, which would break during encoding. Related: - https://github.com/socketio/socket.io-admin-ui/issues/48 - https://github.com/socketio/socket.io-admin-ui/issues/54 --- lib/index.ts | 4 ++ package.json | 2 +- test/events.ts | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ test/util.ts | 10 +++ 4 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 test/events.ts create mode 100644 test/util.ts diff --git a/lib/index.ts b/lib/index.ts index 822ffe6..83f894c 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -368,6 +368,10 @@ const registerVerboseListeners = ( if (nsp !== adminNamespace) { if (typeof socket.onAny === "function") { socket.onAny((...args: any[]) => { + const withAck = typeof args[args.length - 1] === "function"; + if (withAck) { + args = args.slice(0, -1); + } adminNamespace.emit( "event_received", nsp.name, diff --git a/package.json b/package.json index 677bb76..59f503b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "compile": "tsc", "test": "npm run format:check && npm run compile && npm run test:unit", - "test:unit": "nyc mocha --require ts-node/register --timeout 5000 test/index.ts --quit", + "test:unit": "nyc mocha --require ts-node/register --timeout 5000 test/*.ts --quit", "format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'", "format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'", "prepack": "npm run compile" diff --git a/test/events.ts b/test/events.ts new file mode 100644 index 0000000..2536c6e --- /dev/null +++ b/test/events.ts @@ -0,0 +1,167 @@ +import { Server } from "socket.io"; +import { createServer } from "http"; +import { AddressInfo } from "net"; +import { instrument } from "../lib"; +import { io as ioc } from "socket.io-client"; +import expect = require("expect.js"); +import { createPartialDone } from "./util"; + +describe("events", () => { + let port: number, io: Server; + + beforeEach((done) => { + const httpServer = createServer(); + io = new Server(httpServer); + + httpServer.listen(() => { + port = (httpServer.address() as AddressInfo).port; + done(); + }); + }); + + afterEach(() => { + io.close(); + }); + + it("should track events sent to the client", (done) => { + instrument(io, { + auth: false, + }); + + const adminSocket = ioc(`http://localhost:${port}/admin`); + const clientSocket = ioc(`http://localhost:${port}`, { + forceNew: true, + }); + + const partialDone = createPartialDone(2, () => { + adminSocket.disconnect(); + clientSocket.disconnect(); + done(); + }); + + io.on("connection", (socket) => { + socket.emit("hello", 1, "2", Buffer.from([3])); + }); + + clientSocket.on("hello", partialDone); + + adminSocket.on("event_sent", (arg1, arg2, arg3, arg4) => { + expect(arg1).to.eql("/"); + expect(arg2).to.eql(clientSocket.id); + expect(arg3).to.eql(["hello", 1, "2", Buffer.from([3])]); + expect(new Date(arg4).toISOString()).to.eql(arg4); + + partialDone(); + }); + }); + + it("should track events sent to the client (with ack)", (done) => { + instrument(io, { + auth: false, + }); + + const adminSocket = ioc(`http://localhost:${port}/admin`); + const clientSocket = ioc(`http://localhost:${port}`, { + forceNew: true, + }); + + const partialDone = createPartialDone(2, () => { + adminSocket.disconnect(); + clientSocket.disconnect(); + done(); + }); + + io.on("connection", (socket) => { + socket.emit("hello", (arg: string) => { + expect(arg).to.eql("world"); + + partialDone(); + }); + }); + + clientSocket.on("hello", (cb) => { + cb("world"); + }); + + adminSocket.on("event_sent", (arg1, arg2, arg3, arg4) => { + expect(arg1).to.eql("/"); + expect(arg2).to.eql(clientSocket.id); + expect(arg3).to.eql(["hello"]); + expect(new Date(arg4).toISOString()).to.eql(arg4); + + partialDone(); + }); + }); + + it("should track events received from the client", (done) => { + instrument(io, { + auth: false, + }); + + const adminSocket = ioc(`http://localhost:${port}/admin`); + const clientSocket = ioc(`http://localhost:${port}`, { + forceNew: true, + }); + + const partialDone = createPartialDone(2, () => { + adminSocket.disconnect(); + clientSocket.disconnect(); + done(); + }); + + io.on("connection", (socket) => { + socket.on("hello", partialDone); + }); + + adminSocket.on("event_received", (arg1, arg2, arg3, arg4) => { + expect(arg1).to.eql("/"); + expect(arg2).to.eql(clientSocket.id); + expect(arg3).to.eql(["hello", 1, "2", Buffer.from([3])]); + expect(new Date(arg4).toISOString()).to.eql(arg4); + + partialDone(); + }); + + clientSocket.emit("hello", 1, "2", Buffer.from([3])); + }); + + it("should track events received from the client (with ack)", (done) => { + instrument(io, { + auth: false, + }); + + const adminSocket = ioc(`http://localhost:${port}/admin`); + const clientSocket = ioc(`http://localhost:${port}`, { + forceNew: true, + }); + + const partialDone = createPartialDone(2, () => { + adminSocket.disconnect(); + clientSocket.disconnect(); + done(); + }); + + io.on("connection", (socket) => { + socket.on("hello", (arg, cb) => { + expect(arg).to.eql("world"); + + cb("123"); + }); + }); + + adminSocket.on("event_received", (arg1, arg2, arg3, arg4) => { + expect(arg1).to.eql("/"); + expect(arg2).to.eql(clientSocket.id); + expect(arg3).to.eql(["hello", "world"]); + expect(new Date(arg4).toISOString()).to.eql(arg4); + + partialDone(); + }); + + clientSocket.emit("hello", "world", (arg: string) => { + expect(arg).to.eql("123"); + + partialDone(); + }); + }); +}); diff --git a/test/util.ts b/test/util.ts new file mode 100644 index 0000000..e1eff46 --- /dev/null +++ b/test/util.ts @@ -0,0 +1,10 @@ +export function createPartialDone(count: number, done: (err?: Error) => void) { + let i = 0; + return () => { + if (++i === count) { + done(); + } else if (i > count) { + done(new Error(`partialDone() called too many times: ${i} > ${count}`)); + } + }; +}