mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-08 22:48:20 -05:00
373 lines
9.7 KiB
TypeScript
373 lines
9.7 KiB
TypeScript
import { io as ioc, Socket as ClientSocket } from "socket.io-client";
|
|
import expect = require("expect.js");
|
|
import { setupPrimary } from "..";
|
|
import { times, sleep } from "./util";
|
|
import cluster, { Worker } from "node:cluster";
|
|
|
|
const NODES_COUNT = 3;
|
|
|
|
cluster.setupMaster({
|
|
exec: "./test/worker.js",
|
|
// @ts-ignore
|
|
serialization: "advanced", // needed for packets containing buffers
|
|
});
|
|
|
|
setupPrimary();
|
|
|
|
const getRooms = (worker): Promise<Set<string>> => {
|
|
worker.send("get rooms");
|
|
return new Promise((resolve) => {
|
|
worker.once("message", (content) => {
|
|
resolve(content);
|
|
});
|
|
});
|
|
};
|
|
|
|
describe("@socket.io/cluster-adapter", () => {
|
|
let clientSockets: ClientSocket[], workers: Worker[];
|
|
|
|
beforeEach((done) => {
|
|
clientSockets = [];
|
|
workers = [];
|
|
|
|
for (let i = 1; i <= NODES_COUNT; i++) {
|
|
const PORT = 40000 + i;
|
|
const worker = cluster.fork({
|
|
PORT,
|
|
});
|
|
|
|
worker.on("listening", () => {
|
|
const clientSocket = ioc(`http://localhost:${PORT}`);
|
|
|
|
clientSocket.on("connect", async () => {
|
|
workers.push(worker);
|
|
clientSockets.push(clientSocket);
|
|
if (clientSockets.length === NODES_COUNT) {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
for (const id in cluster.workers) {
|
|
cluster.workers[id].kill();
|
|
}
|
|
clientSockets.forEach((socket) => {
|
|
socket.disconnect();
|
|
});
|
|
});
|
|
|
|
describe("broadcast", function () {
|
|
it("broadcasts to all clients", (done) => {
|
|
const partialDone = times(3, done);
|
|
|
|
clientSockets.forEach((clientSocket) => {
|
|
clientSocket.on("test", (arg1, arg2, arg3) => {
|
|
expect(arg1).to.eql(1);
|
|
expect(arg2).to.eql("2");
|
|
expect(Buffer.isBuffer(arg3)).to.be(true);
|
|
partialDone();
|
|
});
|
|
});
|
|
|
|
workers[0].send("broadcasts to all clients");
|
|
});
|
|
|
|
it("broadcasts to all clients in a namespace", (done) => {
|
|
const partialDone = times(3, done);
|
|
|
|
const onConnect = times(3, async () => {
|
|
workers[0].send("broadcasts to all clients in a namespace");
|
|
});
|
|
|
|
clientSockets.forEach((clientSocket) => {
|
|
const socket = clientSocket.io.socket("/custom");
|
|
socket.on("connect", onConnect);
|
|
socket.on("test", () => {
|
|
socket.disconnect();
|
|
partialDone();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("broadcasts to all clients in a room", (done) => {
|
|
workers[1].send("join room1");
|
|
|
|
clientSockets[0].on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
|
|
clientSockets[1].on("test", () => {
|
|
done();
|
|
});
|
|
|
|
clientSockets[2].on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
|
|
workers[0].send("broadcasts to all clients in a room");
|
|
});
|
|
|
|
it("broadcasts to all clients except in room", (done) => {
|
|
const partialDone = times(2, done);
|
|
workers[1].send("join room1");
|
|
|
|
clientSockets[0].on("test", () => {
|
|
partialDone();
|
|
});
|
|
|
|
clientSockets[1].on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
|
|
clientSockets[2].on("test", () => {
|
|
partialDone();
|
|
});
|
|
|
|
workers[0].send("broadcasts to all clients except in room");
|
|
});
|
|
|
|
it("broadcasts to local clients only", (done) => {
|
|
clientSockets[0].on("test", () => {
|
|
done();
|
|
});
|
|
|
|
clientSockets[1].on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
|
|
clientSockets[2].on("test", () => {
|
|
done(new Error("should not happen"));
|
|
});
|
|
|
|
workers[0].send("broadcasts to local clients only");
|
|
});
|
|
|
|
it("broadcasts with multiple acknowledgements", (done) => {
|
|
clientSockets[0].on("test", (cb) => {
|
|
cb(1);
|
|
});
|
|
|
|
clientSockets[1].on("test", (cb) => {
|
|
cb(2);
|
|
});
|
|
|
|
clientSockets[2].on("test", (cb) => {
|
|
cb(3);
|
|
});
|
|
|
|
workers[0].send("broadcasts with multiple acknowledgements");
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
|
|
it("broadcasts with multiple acknowledgements (binary content)", (done) => {
|
|
clientSockets[0].on("test", (cb) => {
|
|
cb(Buffer.from([1]));
|
|
});
|
|
|
|
clientSockets[1].on("test", (cb) => {
|
|
cb(Buffer.from([2]));
|
|
});
|
|
|
|
clientSockets[2].on("test", (cb) => {
|
|
cb(Buffer.from([3]));
|
|
});
|
|
|
|
workers[0].send(
|
|
"broadcasts with multiple acknowledgements (binary content)",
|
|
);
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
|
|
it("broadcasts with multiple acknowledgements (no client)", (done) => {
|
|
workers[0].send("broadcasts with multiple acknowledgements (no client)");
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
|
|
it("broadcasts with multiple acknowledgements (timeout)", (done) => {
|
|
clientSockets[0].on("test", (cb) => {
|
|
cb(1);
|
|
});
|
|
|
|
clientSockets[1].on("test", (cb) => {
|
|
cb(2);
|
|
});
|
|
|
|
clientSockets[2].on("test", (cb) => {
|
|
// do nothing
|
|
});
|
|
|
|
workers[0].send("broadcasts with multiple acknowledgements (timeout)");
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("socketsJoin", () => {
|
|
it("makes all socket instances join the specified room", async () => {
|
|
workers[0].send("makes all socket instances join the specified room");
|
|
|
|
await sleep(100);
|
|
|
|
expect((await getRooms(workers[0])).has("room1")).to.be(true);
|
|
expect((await getRooms(workers[1])).has("room1")).to.be(true);
|
|
expect((await getRooms(workers[2])).has("room1")).to.be(true);
|
|
});
|
|
|
|
it("makes the matching socket instances join the specified room", async () => {
|
|
workers[0].send("join room1");
|
|
workers[2].send("join room1");
|
|
|
|
workers[0].send(
|
|
"makes the matching socket instances join the specified room",
|
|
);
|
|
|
|
await sleep(100);
|
|
|
|
expect((await getRooms(workers[0])).has("room2")).to.be(true);
|
|
expect((await getRooms(workers[1])).has("room2")).to.be(false);
|
|
expect((await getRooms(workers[2])).has("room2")).to.be(true);
|
|
});
|
|
});
|
|
|
|
describe("socketsLeave", () => {
|
|
it("makes all socket instances leave the specified room", async () => {
|
|
workers[0].send("join room1");
|
|
workers[2].send("join room1");
|
|
|
|
workers[0].send("makes all socket instances leave the specified room");
|
|
|
|
await sleep(100);
|
|
|
|
expect((await getRooms(workers[0])).has("room1")).to.be(false);
|
|
expect((await getRooms(workers[1])).has("room1")).to.be(false);
|
|
expect((await getRooms(workers[2])).has("room1")).to.be(false);
|
|
});
|
|
|
|
it("makes the matching socket instances leave the specified room", async () => {
|
|
workers[0].send("join room1 & room2");
|
|
workers[2].send("join room2");
|
|
|
|
workers[0].send(
|
|
"makes the matching socket instances leave the specified room",
|
|
);
|
|
|
|
await sleep(100);
|
|
|
|
expect((await getRooms(workers[0])).has("room2")).to.be(false);
|
|
expect((await getRooms(workers[1])).has("room2")).to.be(false);
|
|
expect((await getRooms(workers[2])).has("room2")).to.be(true);
|
|
});
|
|
});
|
|
|
|
describe("disconnectSockets", () => {
|
|
it("makes all socket instances disconnect", (done) => {
|
|
const partialDone = times(3, done);
|
|
|
|
clientSockets.forEach((clientSocket) => {
|
|
clientSocket.on("disconnect", (reason) => {
|
|
expect(reason).to.eql("io server disconnect");
|
|
partialDone();
|
|
});
|
|
});
|
|
|
|
workers[0].send("makes all socket instances disconnect");
|
|
});
|
|
});
|
|
|
|
describe("fetchSockets", () => {
|
|
it("returns all socket instances", (done) => {
|
|
workers[0].send("returns all socket instances");
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("serverSideEmit", () => {
|
|
it("sends an event to other server instances", (done) => {
|
|
const partialDone = times(2, done);
|
|
|
|
workers[0].send("sends an event to other server instances");
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done(new Error("should not happen"));
|
|
}
|
|
});
|
|
|
|
workers[1].on("message", (result) => {
|
|
expect(result).to.eql("ok");
|
|
partialDone();
|
|
});
|
|
|
|
workers[2].on("message", (result) => {
|
|
expect(result).to.eql("ok");
|
|
partialDone();
|
|
});
|
|
});
|
|
|
|
it("sends an event and receives a response from the other server instances", (done) => {
|
|
workers[0].send(
|
|
"sends an event and receives a response from the other server instances (1)",
|
|
);
|
|
workers[1].send(
|
|
"sends an event and receives a response from the other server instances (2)",
|
|
);
|
|
workers[2].send(
|
|
"sends an event and receives a response from the other server instances (3)",
|
|
);
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
|
|
it("sends an event but timeout if one server does not respond", function (done) {
|
|
this.timeout(6000); // currently not possible to configure the timeout delay
|
|
|
|
workers[0].send(
|
|
"sends an event but timeout if one server does not respond (1)",
|
|
);
|
|
workers[1].send(
|
|
"sends an event but timeout if one server does not respond (2)",
|
|
);
|
|
workers[2].send(
|
|
"sends an event but timeout if one server does not respond (3)",
|
|
);
|
|
|
|
workers[0].on("message", (result) => {
|
|
if (result === "ok") {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|