mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
927 lines
26 KiB
TypeScript
927 lines
26 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { Namespace, Server, Socket } from "socket.io";
|
|
import type { SocketId } from "socket.io-adapter";
|
|
import { createClient, createPartialDone, fail, success } from "./support/util.ts";
|
|
|
|
// Hanging tests are disabled because they cause the test suite to hang
|
|
describe.skip("namespaces", () => {
|
|
it("should be accessible through .sockets", done => {
|
|
const io = new Server();
|
|
expect(io.sockets).toBeInstanceOf(Namespace);
|
|
done();
|
|
});
|
|
|
|
it("should be aliased", done => {
|
|
const io = new Server();
|
|
expect(typeof io.use).toBe("function");
|
|
expect(typeof io.to).toBe("function");
|
|
expect(typeof io["in"]).toBe("function");
|
|
expect(typeof io.emit).toBe("function");
|
|
expect(typeof io.send).toBe("function");
|
|
expect(typeof io.write).toBe("function");
|
|
expect(typeof io.allSockets).toBe("function");
|
|
expect(typeof io.compress).toBe("function");
|
|
done();
|
|
});
|
|
|
|
it("should return an immutable broadcast operator", done => {
|
|
const io = new Server();
|
|
const operator = io.local.to(["room1", "room2"]).except("room3");
|
|
operator.compress(true).emit("hello");
|
|
operator.volatile.emit("hello");
|
|
operator.to("room4").emit("hello");
|
|
operator.except("room5").emit("hello");
|
|
io.to("room6").emit("hello");
|
|
// @ts-ignore
|
|
expect(operator.rooms).toStrictEqual(new Set(["room1", "room2"]));
|
|
// @ts-ignore
|
|
expect(operator.exceptRooms).toStrictEqual(new Set(["room3"]));
|
|
// @ts-ignore
|
|
expect(operator.flags).toStrictEqual({ local: true });
|
|
|
|
done();
|
|
});
|
|
|
|
it("should automatically connect", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
socket.on("connect", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket);
|
|
});
|
|
});
|
|
|
|
it("should fire a `connection` event", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
io.on("connection", s => {
|
|
clearTimeout(timeout);
|
|
expect(s).toBeInstanceOf(Socket);
|
|
success(done, io, socket);
|
|
});
|
|
});
|
|
|
|
it("should fire a `connect` event", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
io.on("connect", s => {
|
|
clearTimeout(timeout);
|
|
expect(s).toBeInstanceOf(Socket);
|
|
success(done, io, socket);
|
|
});
|
|
});
|
|
|
|
it("should work with many sockets", done => {
|
|
const io = new Server(0);
|
|
io.of("/chat");
|
|
io.of("/news");
|
|
const chat = createClient(io, "/chat");
|
|
const news = createClient(io, "/news");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), chat, news);
|
|
}, 300);
|
|
|
|
let total = 2;
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, chat, news);
|
|
}
|
|
|
|
chat.on("connect", () => {
|
|
--total || _success();
|
|
});
|
|
news.on("connect", () => {
|
|
--total || _success();
|
|
});
|
|
});
|
|
|
|
it('should be able to equivalently start with "" or "/" on server', done => {
|
|
const io = new Server(0);
|
|
const c1 = createClient(io, "/");
|
|
const c2 = createClient(io, "/abc");
|
|
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1, c2);
|
|
}, 300);
|
|
|
|
let total = 2;
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, c1, c2);
|
|
}
|
|
|
|
io.of("").on("connection", () => {
|
|
--total || _success();
|
|
});
|
|
io.of("abc").on("connection", () => {
|
|
--total || _success();
|
|
});
|
|
});
|
|
|
|
it('should be equivalent for "" and "/" on client', done => {
|
|
const io = new Server(0);
|
|
const c1 = createClient(io, "");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1);
|
|
}, 300);
|
|
io.of("/").on("connection", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, c1);
|
|
});
|
|
});
|
|
|
|
it("should work with `of` and many sockets", done => {
|
|
const io = new Server(0);
|
|
const chat = createClient(io, "/chat");
|
|
const news = createClient(io, "/news");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), chat, news);
|
|
}, 300);
|
|
|
|
let total = 2;
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, chat, news);
|
|
}
|
|
io.of("/news").on("connection", socket => {
|
|
try {
|
|
expect(socket).toBeInstanceOf(Socket);
|
|
--total || _success();
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, chat, news);
|
|
}
|
|
});
|
|
io.of("/news").on("connection", socket => {
|
|
try {
|
|
expect(socket).toBeInstanceOf(Socket);
|
|
--total || _success();
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, chat, news);
|
|
}
|
|
});
|
|
});
|
|
|
|
it.skip("should work with `of` second param", done => {
|
|
const io = new Server(0);
|
|
const chat = createClient(io, "/chat");
|
|
const news = createClient(io, "/news");
|
|
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), chat, news);
|
|
}, 300);
|
|
|
|
let total = 2;
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, chat, news);
|
|
}
|
|
io.of("/news", socket => {
|
|
try {
|
|
expect(socket).toBeInstanceOf(Socket);
|
|
--total || _success();
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, chat, news);
|
|
}
|
|
});
|
|
io.of("/news", socket => {
|
|
try {
|
|
expect(socket).toBeInstanceOf(Socket);
|
|
--total || _success();
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, chat, news);
|
|
}
|
|
});
|
|
});
|
|
|
|
it.skip("should disconnect upon transport disconnection", done => {
|
|
const io = new Server(0);
|
|
const chat = createClient(io, "/chat");
|
|
const news = createClient(io, "/news");
|
|
|
|
let total = 2;
|
|
let totald = 2;
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), chat, news);
|
|
}, 300);
|
|
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, chat, news);
|
|
}
|
|
|
|
function close() {
|
|
s.disconnect(true);
|
|
}
|
|
|
|
let s: Socket;
|
|
io.of("/news", socket => {
|
|
socket.on("disconnect", reason => {
|
|
--totald || _success();
|
|
});
|
|
--total || close();
|
|
});
|
|
io.of("/chat", socket => {
|
|
s = socket;
|
|
socket.on("disconnect", reason => {
|
|
--totald || _success();
|
|
});
|
|
--total || close();
|
|
});
|
|
});
|
|
|
|
it.skip("should fire a `disconnecting` event just before leaving all rooms", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
io.on("connection", s => {
|
|
s.join("a");
|
|
// FIXME not sure why process.nextTick() is needed here
|
|
process.nextTick(() => s.disconnect());
|
|
|
|
let total = 2;
|
|
|
|
function _success() {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket);
|
|
}
|
|
|
|
s.on("disconnecting", reason => {
|
|
try {
|
|
expect(s.rooms).toStrictEqual(new Set([s.id, "a"]));
|
|
total--;
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
|
|
s.on("disconnect", reason => {
|
|
try {
|
|
expect(s.rooms.size).toBe(0);
|
|
--total || _success();
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it.skip("should return error connecting to non-existent namespace", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/doesnotexist");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
socket.on("connect_error", err => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(err.message).toBe("Invalid namespace");
|
|
success(done, io);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
});
|
|
|
|
it.skip("should not reuse same-namespace connections", done => {
|
|
const io = new Server(0);
|
|
const clientSocket1 = createClient(io);
|
|
const clientSocket2 = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), clientSocket1, clientSocket2);
|
|
}, 300);
|
|
|
|
let connections = 0;
|
|
io.on("connection", () => {
|
|
connections++;
|
|
if (connections === 2) {
|
|
clearTimeout(timeout);
|
|
success(done, io, clientSocket1, clientSocket2);
|
|
}
|
|
});
|
|
});
|
|
|
|
it.skip("should find all clients in a namespace", done => {
|
|
const io = new Server(0);
|
|
const chatSids: string[] = [];
|
|
let otherSid: SocketId | null = null;
|
|
|
|
const c1 = createClient(io, "/chat");
|
|
const c2 = createClient(io, "/chat", { forceNew: true });
|
|
const c3 = createClient(io, "/other", { forceNew: true });
|
|
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1, c2, c3);
|
|
}, 300);
|
|
|
|
let total = 3;
|
|
io.of("/chat").on("connection", socket => {
|
|
chatSids.push(socket.id);
|
|
--total || getSockets();
|
|
});
|
|
io.of("/other").on("connection", socket => {
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
|
|
async function getSockets() {
|
|
const sids = await io.of("/chat").allSockets();
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(sids).toStrictEqual(new Set([chatSids[0], chatSids[1]]));
|
|
expect(sids).not.toContain(otherSid);
|
|
success(done, io, c1, c2, c3);
|
|
} catch (err) {
|
|
fail(done, io, err, c1, c2, c3);
|
|
}
|
|
}
|
|
});
|
|
|
|
it.skip("should find all clients in a namespace room", done => {
|
|
const io = new Server(0);
|
|
let chatFooSid: SocketId | null = null;
|
|
let chatBarSid: SocketId | null = null;
|
|
let otherSid: SocketId | null = null;
|
|
|
|
const c1 = createClient(io, "/chat");
|
|
const c2 = createClient(io, "/chat", { forceNew: true });
|
|
const c3 = createClient(io, "/other", { forceNew: true });
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1, c2, c3);
|
|
}, 300);
|
|
|
|
let chatIndex = 0;
|
|
let total = 3;
|
|
io.of("/chat").on("connection", socket => {
|
|
if (chatIndex++) {
|
|
socket.join("foo");
|
|
chatFooSid = socket.id;
|
|
--total || getSockets();
|
|
} else {
|
|
socket.join("bar");
|
|
chatBarSid = socket.id;
|
|
--total || getSockets();
|
|
}
|
|
});
|
|
io.of("/other").on("connection", socket => {
|
|
socket.join("foo");
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
|
|
async function getSockets() {
|
|
const sids = await io.of("/chat").in("foo").allSockets();
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(sids).toStrictEqual(new Set([chatFooSid]));
|
|
expect(sids).not.toContain(chatBarSid);
|
|
expect(sids).not.toContain(otherSid);
|
|
success(done, io, c1, c2, c3);
|
|
} catch (err) {
|
|
fail(done, io, err, c1, c2, c3);
|
|
}
|
|
}
|
|
});
|
|
|
|
it.skip("should find all clients across namespace rooms", done => {
|
|
const io = new Server(0);
|
|
let chatFooSid: SocketId | null = null;
|
|
let chatBarSid: SocketId | null = null;
|
|
let otherSid: SocketId | null = null;
|
|
|
|
const c1 = createClient(io, "/chat");
|
|
const c2 = createClient(io, "/chat", { forceNew: true });
|
|
const c3 = createClient(io, "/other", { forceNew: true });
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1, c2, c3);
|
|
}, 300);
|
|
|
|
let chatIndex = 0;
|
|
let total = 3;
|
|
io.of("/chat").on("connection", socket => {
|
|
if (chatIndex++) {
|
|
socket.join("foo");
|
|
chatFooSid = socket.id;
|
|
--total || getSockets();
|
|
} else {
|
|
socket.join("bar");
|
|
chatBarSid = socket.id;
|
|
--total || getSockets();
|
|
}
|
|
});
|
|
io.of("/other").on("connection", socket => {
|
|
socket.join("foo");
|
|
otherSid = socket.id;
|
|
--total || getSockets();
|
|
});
|
|
|
|
async function getSockets() {
|
|
const sids = await io.of("/chat").allSockets();
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(sids).toStrictEqual(new Set([chatFooSid, chatBarSid]));
|
|
expect(sids).not.toContain(otherSid);
|
|
success(done, io, c1, c2, c3);
|
|
} catch (err) {
|
|
fail(done, io, err, c1, c2, c3);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("should not emit volatile event after regular event", done => {
|
|
const io = new Server(0);
|
|
|
|
let counter = 0;
|
|
io.of("/chat").on("connection", s => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
io.of("/chat").emit("ev", "data");
|
|
io.of("/chat").volatile.emit("ev", "data");
|
|
}, 50);
|
|
});
|
|
|
|
const socket = createClient(io, "/chat");
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
|
|
setTimeout(() => {
|
|
try {
|
|
expect(counter).toBe(1);
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
}, 300);
|
|
});
|
|
|
|
it("should emit volatile event", done => {
|
|
const io = new Server(0);
|
|
|
|
let counter = 0;
|
|
io.of("/chat").on("connection", s => {
|
|
// Wait to make sure there are no packets being sent for opening the connection
|
|
setTimeout(() => {
|
|
io.of("/chat").volatile.emit("ev", "data");
|
|
}, 100);
|
|
});
|
|
|
|
const socket = createClient(io, "/chat");
|
|
socket.on("ev", () => {
|
|
counter++;
|
|
});
|
|
|
|
setTimeout(() => {
|
|
try {
|
|
expect(counter).toBe(1);
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
}, 300);
|
|
});
|
|
|
|
it("should enable compression by default", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/chat");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
io.of("/chat").on("connection", s => {
|
|
s.conn.once("packetCreate", packet => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(packet.options.compress).toBe(true);
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
io.of("/chat").emit("woot", "hi");
|
|
});
|
|
});
|
|
|
|
it("should disable compression", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/chat");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
io.of("/chat").on("connection", s => {
|
|
s.conn.once("packetCreate", packet => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(packet.options.compress).toBe(false);
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
io.of("/chat").compress(false).emit("woot", "hi");
|
|
});
|
|
});
|
|
|
|
it("should throw on reserved event", () => {
|
|
const io = new Server();
|
|
|
|
expect(() => io.emit("connect")).toThrow(/"connect" is a reserved event name/);
|
|
});
|
|
|
|
it("should close a client without namespace", done => {
|
|
const io = new Server(0, {
|
|
connectTimeout: 10,
|
|
});
|
|
|
|
const socket = createClient(io);
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
// @ts-ignore
|
|
socket.io.engine.write = () => {}; // prevent the client from sending a CONNECT packet
|
|
|
|
socket.on("disconnect", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket);
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific socket when emitting", done => {
|
|
const io = new Server(0);
|
|
|
|
const socket1 = createClient(io, "/");
|
|
const socket2 = createClient(io, "/");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket1, socket2);
|
|
}, 300);
|
|
|
|
socket2.on("a", () => {
|
|
clearTimeout(timeout);
|
|
fail(done, io, new Error("should not happen"), socket1, socket2);
|
|
});
|
|
socket1.on("a", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket1, socket2);
|
|
});
|
|
|
|
socket2.on("connect", () => {
|
|
io.except(socket2.id).emit("a");
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific socket when emitting (in a namespace)", done => {
|
|
const io = new Server(0);
|
|
|
|
const nsp = io.of("/nsp");
|
|
|
|
const socket1 = createClient(io, "/nsp");
|
|
const socket2 = createClient(io, "/nsp");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket1, socket2);
|
|
}, 300);
|
|
|
|
socket2.on("a", () => {
|
|
clearTimeout(timeout);
|
|
fail(done, io, new Error("should not happen"), socket1, socket2);
|
|
});
|
|
socket1.on("a", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket1, socket2);
|
|
});
|
|
|
|
socket2.on("connect", () => {
|
|
nsp.except(socket2.id).emit("a");
|
|
});
|
|
});
|
|
|
|
it("should exclude a specific room when emitting", done => {
|
|
const io = new Server(0);
|
|
|
|
const nsp = io.of("/nsp");
|
|
|
|
const socket1 = createClient(io, "/nsp");
|
|
const socket2 = createClient(io, "/nsp");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket1, socket2);
|
|
}, 300);
|
|
|
|
socket1.on("a", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket1, socket2);
|
|
});
|
|
socket2.on("a", () => {
|
|
clearTimeout(timeout);
|
|
fail(done, io, new Error("should not happen"), socket1, socket2);
|
|
});
|
|
|
|
nsp.on("connection", socket => {
|
|
socket.on("broadcast", () => {
|
|
socket.join("room1");
|
|
nsp.except("room1").emit("a");
|
|
});
|
|
});
|
|
|
|
socket2.emit("broadcast");
|
|
});
|
|
|
|
it("should emit an 'new_namespace' event", done => {
|
|
const io = new Server();
|
|
|
|
io.on("new_namespace", namespace => {
|
|
expect(namespace.name).toBe("/nsp");
|
|
done();
|
|
});
|
|
|
|
io.of("/nsp");
|
|
});
|
|
|
|
it("should not clean up a non-dynamic namespace", done => {
|
|
const io = new Server(0, { cleanupEmptyChildNamespaces: true });
|
|
const c1 = createClient(io, "/chat");
|
|
|
|
c1.on("connect", () => {
|
|
c1.disconnect();
|
|
|
|
// Give it some time to disconnect the client
|
|
setTimeout(() => {
|
|
try {
|
|
expect(io._nsps.has("/chat")).toBe(true);
|
|
expect(io._nsps.get("/chat")!.sockets.size).toBe(0);
|
|
success(done, io);
|
|
} catch (err) {
|
|
fail(done, io, err);
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
io.of("/chat");
|
|
});
|
|
|
|
describe("dynamic namespaces", () => {
|
|
it.skip("should allow connections to dynamic namespaces with a regex", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
const partialDone = createPartialDone(4, () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket);
|
|
});
|
|
|
|
let dynamicNsp = io
|
|
.of(/^\/dynamic-\d+$/)
|
|
.on("connect", socket => {
|
|
try {
|
|
expect(socket.nsp.name).toBe("/dynamic-101");
|
|
dynamicNsp.emit("hello", 1, "2", { 3: "4" });
|
|
partialDone();
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
})
|
|
.use((socket, next) => {
|
|
next();
|
|
partialDone();
|
|
});
|
|
socket.on("connect_error", err => {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, socket);
|
|
});
|
|
socket.on("connect", () => {
|
|
partialDone();
|
|
});
|
|
socket.on("hello", (a, b, c) => {
|
|
try {
|
|
expect(a).toBe(1);
|
|
expect(b).toBe("2");
|
|
expect(c).toStrictEqual({ 3: "4" });
|
|
partialDone();
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
});
|
|
|
|
it.skip("should allow connections to dynamic namespaces with a function", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
io.of((name, query, next) => next(null, "/dynamic-101" === name));
|
|
socket.on("connect", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, socket);
|
|
});
|
|
});
|
|
|
|
it("should disallow connections when no dynamic namespace matches", done => {
|
|
const io = new Server(0);
|
|
const socket = createClient(io, "/abc");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
|
|
io.of(/^\/dynamic-\d+$/);
|
|
io.of((name, query, next) => next(null, "/dynamic-101" === name));
|
|
|
|
socket.on("connect_error", err => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(err.message).toBe("Invalid namespace");
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should emit an 'new_namespace' event for a dynamic namespace", done => {
|
|
const io = new Server(0);
|
|
io.of(/^\/dynamic-\d+$/);
|
|
const socket = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), socket);
|
|
}, 300);
|
|
io.on("new_namespace", namespace => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(namespace.name).toBe("/dynamic-101");
|
|
|
|
success(done, io, socket);
|
|
} catch (err) {
|
|
fail(done, io, err, socket);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should handle race conditions with dynamic namespaces (#4136)", done => {
|
|
const io = new Server(0);
|
|
let timeout: Timer;
|
|
const counters = {
|
|
connected: 0,
|
|
created: 0,
|
|
events: 0,
|
|
};
|
|
const buffer: Function[] = [];
|
|
io.on("new_namespace", namespace => {
|
|
counters.created++;
|
|
});
|
|
|
|
const handler = () => {
|
|
if (++counters.events === 2) {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(counters.created).toBe(1);
|
|
success(done, io, one, two);
|
|
} catch (err) {
|
|
fail(done, io, err, one, two);
|
|
}
|
|
}
|
|
};
|
|
|
|
io.of((name, query, next) => {
|
|
buffer.push(next);
|
|
if (buffer.length === 2) {
|
|
buffer.forEach(next => next(null, true));
|
|
}
|
|
}).on("connection", socket => {
|
|
if (++counters.connected === 2) {
|
|
io.of("/dynamic-101").emit("message");
|
|
}
|
|
});
|
|
|
|
let one = createClient(io, "/dynamic-101");
|
|
let two = createClient(io, "/dynamic-101");
|
|
timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), one, two);
|
|
}, 300);
|
|
one.on("message", handler);
|
|
two.on("message", handler);
|
|
});
|
|
|
|
it("should clean up namespace when cleanupEmptyChildNamespaces is on and there are no more sockets in a namespace", done => {
|
|
const io = new Server(0, { cleanupEmptyChildNamespaces: true });
|
|
const c1 = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1);
|
|
}, 300);
|
|
|
|
c1.on("connect", () => {
|
|
c1.disconnect();
|
|
|
|
// Give it some time to disconnect and clean up the namespace
|
|
setTimeout(() => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(io._nsps.has("/dynamic-101")).toBe(false);
|
|
success(done, io);
|
|
} catch (err) {
|
|
fail(done, io, err, c1);
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
io.of(/^\/dynamic-\d+$/);
|
|
});
|
|
|
|
it.skip("should allow a client to connect to a cleaned up namespace", done => {
|
|
const io = new Server(0, { cleanupEmptyChildNamespaces: true });
|
|
const c1 = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1);
|
|
}, 300);
|
|
c1.on("connect", () => {
|
|
c1.disconnect();
|
|
|
|
// Give it some time to disconnect and clean up the namespace
|
|
setTimeout(() => {
|
|
try {
|
|
expect(io._nsps.has("/dynamic-101")).toBe(false);
|
|
|
|
const c2 = createClient(io, "/dynamic-101");
|
|
|
|
c2.on("connect", () => {
|
|
clearTimeout(timeout);
|
|
success(done, io, c2);
|
|
});
|
|
|
|
c2.on("connect_error", () => {
|
|
clearTimeout(timeout);
|
|
fail(done, io, new Error("Client got error when connecting to dynamic namespace"), c1);
|
|
});
|
|
} catch (err) {
|
|
clearTimeout(timeout);
|
|
fail(done, io, err, c1);
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
io.of(/^\/dynamic-\d+$/);
|
|
});
|
|
|
|
it("should not clean up namespace when cleanupEmptyChildNamespaces is off and there are no more sockets in a namespace", done => {
|
|
const io = new Server(0);
|
|
const c1 = createClient(io, "/dynamic-101");
|
|
const timeout = setTimeout(() => {
|
|
fail(done, io, new Error("timeout"), c1);
|
|
}, 300);
|
|
c1.on("connect", () => {
|
|
c1.disconnect();
|
|
|
|
// Give it some time to disconnect and clean up the namespace
|
|
setTimeout(() => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
expect(io._nsps.has("/dynamic-101")).toBe(true);
|
|
expect(io._nsps.get("/dynamic-101")!.sockets.size).toBe(0);
|
|
success(done, io);
|
|
} catch (err) {
|
|
fail(done, io, err, c1);
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
io.of(/^\/dynamic-\d+$/);
|
|
});
|
|
|
|
it("should attach a child namespace to its parent upon manual creation", done => {
|
|
const io = new Server(0);
|
|
const parentNamespace = io.of(/^\/dynamic-\d+$/);
|
|
const childNamespace = io.of("/dynamic-101");
|
|
|
|
try {
|
|
// @ts-ignore
|
|
expect(parentNamespace.children.has(childNamespace)).toBe(true);
|
|
success(done, io);
|
|
} catch (err) {
|
|
fail(done, io, err);
|
|
}
|
|
});
|
|
});
|
|
});
|