Files
bun.sh/test/js/bun/udp/udp_socket.test.ts

240 lines
7.3 KiB
TypeScript

import { udpSocket } from "bun";
import { describe, expect, test } from "bun:test";
import { disableAggressiveGCScope, randomPort } from "harness";
import { dataCases, dataTypes } from "./testdata";
describe("udpSocket()", () => {
test("connect with invalid hostname rejects", async () => {
expect(async () =>
udpSocket({
connect: { hostname: "example!!!!!.com", port: 443 },
}),
).toThrow();
});
test("can create a socket", async () => {
const socket = await udpSocket({});
expect(socket).toBeInstanceOf(Object);
expect(socket.port).toBeInteger();
expect(socket.port).toBeWithin(1, 65535 + 1);
expect(socket.port).toBe(socket.port); // test that property is cached
expect(socket.hostname).toBeString();
expect(socket.hostname).toBe(socket.hostname); // test that property is cached
expect(socket.address).toMatchObject({
address: socket.hostname,
family: socket.hostname === "::" ? "IPv6" : "IPv4",
port: socket.port,
});
expect(socket.address).toBe(socket.address); // test that property is cached
expect(socket.binaryType).toBe("buffer");
expect(socket.binaryType).toBe(socket.binaryType); // test that property is cached
expect(socket.ref).toBeFunction();
expect(socket.unref).toBeFunction();
expect(socket.send).toBeFunction();
expect(socket.close).toBeFunction();
socket.close();
});
test("can create a socket with given port", async () => {
for (let i = 0; i < 30; i++) {
const port = randomPort();
try {
const socket = await udpSocket({ port });
expect(socket.port).toBe(port);
expect(socket.address).toMatchObject({ port: socket.port });
socket.close();
break;
} catch (e) {
continue;
}
}
});
test("can create a socket with a random port", async () => {
const socket = await udpSocket({ port: 0 });
expect(socket.port).toBeInteger();
expect(socket.port).toBeWithin(1, 65535 + 1);
expect(socket.address).toMatchObject({ port: socket.port });
socket.close();
});
describe.each([{ hostname: "localhost" }, { hostname: "127.0.0.1" }, { hostname: "::1" }])(
"can create a socket with given hostname",
({ hostname }) => {
test(hostname, async () => {
const socket = await udpSocket({ hostname });
expect(socket.hostname).toBe(hostname);
expect(socket.port).toBeInteger();
expect(socket.port).toBeWithin(1, 65535 + 1);
expect(socket.address).toMatchObject({ port: socket.port });
socket.close();
});
},
);
const validateRecv = (socket, data, port, address, binaryType, bytes) => {
// This test file takes 1 minute in CI because we are running GC too much.
using _ = disableAggressiveGCScope();
expect(socket).toBeInstanceOf(Object);
expect(socket.binaryType).toBe(binaryType || "buffer");
expect(data.byteLength).toBe(bytes.byteLength);
expect(data).toBeBinaryType(binaryType || "buffer");
expect(data).toEqual(bytes);
expect(port).toBeInteger();
expect(port).toBeWithin(1, 65535 + 1);
expect(port).not.toBe(socket.port);
expect(address).toBeString();
expect(address).not.toBeEmpty();
};
const validateSend = res => {
// This test file takes 1 minute in CI because we are running GC too much.
using _ = disableAggressiveGCScope();
expect(res).toBeBoolean();
};
const validateSendMany = (res, count) => {
// This test file takes 1 minute in CI because we are running GC too much.
using _ = disableAggressiveGCScope();
expect(res).toBeNumber();
expect(res).toBeGreaterThanOrEqual(0);
expect(res).toBeLessThanOrEqual(count);
};
for (const { binaryType, type } of dataTypes) {
for (let { label, data, bytes } of dataCases) {
if (type === ArrayBuffer) {
bytes = new Uint8Array(bytes).buffer;
}
test(`send ${label} (${binaryType || "undefined"})`, async done => {
const client = await udpSocket({});
const server = await udpSocket({
binaryType: binaryType,
socket: {
data(socket, data, port, address) {
validateRecv(socket, data, port, address, binaryType, bytes);
server.close();
client.close();
done();
},
},
});
// handle unreliable transmission in UDP
function sendRec() {
if (!client.closed) {
validateSend(client.send(data, server.port, "127.0.0.1"));
setTimeout(sendRec, 10);
}
}
sendRec();
});
test(`send connected ${label} (${binaryType || "undefined"})`, async done => {
let client;
const server = await udpSocket({
binaryType: binaryType,
socket: {
data(socket, data, port, address) {
validateRecv(socket, data, port, address, binaryType, bytes);
server.close();
client.close();
done();
},
},
});
client = await udpSocket({
connect: {
port: server.port,
hostname: "127.0.0.1",
},
});
// handle unreliable transmission in UDP
function sendRec() {
if (!client.closed) {
validateSend(client.send(data));
setTimeout(sendRec, 10);
}
}
sendRec();
});
test(`sendMany ${label} (${binaryType || "undefined"})`, async done => {
const client = await udpSocket({});
let count = 0;
const server = await udpSocket({
binaryType: binaryType,
socket: {
data(socket, data, port, address) {
validateRecv(socket, data, port, address, binaryType, bytes);
count += 1;
if (count === 100) {
server.close();
client.close();
done();
}
},
},
});
const payload = Array(100).fill([data, server.port, "127.0.0.1"]).flat();
// handle unreliable transmission in UDP
function sendRec() {
if (!client.closed) {
validateSendMany(client.sendMany(payload), 100);
setTimeout(sendRec, 10);
}
}
sendRec();
});
test(`sendMany connected ${label} (${binaryType || "undefined"})`, async done => {
// const client = await udpSocket({});
let client;
let count = 0;
const server = await udpSocket({
binaryType: binaryType,
socket: {
data(socket, data, port, address) {
validateRecv(socket, data, port, address, binaryType, bytes);
count += 1;
if (count === 100) {
server.close();
client.close();
done();
}
},
},
});
client = await udpSocket({
connect: {
port: server.port,
hostname: "127.0.0.1",
},
});
const payload = Array(100).fill(data);
// handle unreliable transmission in UDP
function sendRec() {
if (!client.closed) {
validateSendMany(client.sendMany(payload), 100);
setTimeout(sendRec, 10);
}
}
sendRec();
});
}
}
});