Files
video_socket-server/client/test/unit/signaling.test.js
2026-05-25 20:37:36 +08:00

485 lines
18 KiB
JavaScript

import { jest } from '@jest/globals';
import * as Path from 'path';
import { setup, teardown } from 'jest-dev-server';
import { Signaling, WebSocketSignaling } from "../../src/core/signaling.js";
import { MockSignaling, reset } from "../mocks/mocksignaling.js";
import { waitFor, sleep, serverExeName } from "../helpers/testutils.js";
const portNumber = 8081;
jest.setTimeout(10000);
describe.each([
{ mode: "mock" },
{ mode: "http" },
{ mode: "websocket" },
])('signaling test in public mode', ({ mode }) => {
let signaling1;
let signaling2;
const connectionId1 = "12345";
const connectionId2 = "67890";
const testsdp = "test sdp";
const testcandidate = "test candidate";
beforeAll(async () => {
if (mode == "mock") {
reset(false);
signaling1 = new MockSignaling(1);
signaling2 = new MockSignaling(1);
} else {
const path = Path.resolve(`../bin~/${serverExeName()}`);
let cmd = `${path} -p ${portNumber}`;
if (mode == "http") {
cmd += " -t http";
}
await setup({ command: cmd, port: portNumber, usedPortAction: 'error' });
if (mode == "http") {
signaling1 = new Signaling(1);
signaling2 = new Signaling(1);
}
if (mode == "websocket") {
signaling1 = new WebSocketSignaling(1);
signaling2 = new WebSocketSignaling(1);
}
}
await signaling1.start();
await signaling2.start();
});
afterAll(async () => {
await signaling1.stop();
await signaling2.stop();
signaling1 = null;
signaling2 = null;
if (mode == "mock") {
return;
}
await teardown();
// work around for linux, waitng kill server process
await sleep(1000);
});
test(`onConnect using ${mode}`, async () => {
const signaling1Spy = jest.spyOn(signaling1, 'dispatchEvent');
let connectRes;
let disconnectRes;
signaling1.addEventListener('connect', (e) => connectRes = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes = e.detail);
await signaling1.createConnection(connectionId1);
await waitFor(() => connectRes != null);
expect(connectRes.connectionId).toBe(connectionId1);
expect(connectRes.polite).toBe(true);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes != null);
expect(disconnectRes.connectionId).toBe(connectionId1);
const disconnectCalledCount = signaling1Spy.mock.calls.map(x => x[0].type).filter(x => x == "disconnect").length;
expect(disconnectCalledCount).toBe(1);
signaling1Spy.mockRestore();
});
test(`onOffer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
expect(connectRes1.connectionId).toBe(connectionId1);
expect(connectRes2.connectionId).toBe(connectionId2);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.polite).toBe(false);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
expect(disconnectRes1.connectionId).toBe(connectionId1);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
expect(disconnectRes2.connectionId).toBe(connectionId2);
});
test(`onAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.sdp).toBe(testsdp);
signaling2.sendAnswer(connectionId1, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId1);
expect(answerRes1.sdp).toBe(testsdp);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
});
test(`onCandidate using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
let candidateRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling1.addEventListener('candidate', (e) => candidateRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let candidateRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
signaling2.addEventListener('candidate', (e) => candidateRes2 = e.detail);
await signaling1.createConnection(connectionId1);
await signaling2.createConnection(connectionId2);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId1, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId1);
expect(offerRes2.sdp).toBe(testsdp);
signaling2.sendAnswer(connectionId1, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId1);
expect(answerRes1.sdp).toBe(testsdp);
await signaling2.sendCandidate(connectionId1, testcandidate, 1, 1);
await waitFor(() => candidateRes1 != null);
expect(candidateRes1.connectionId).toBe(connectionId1);
expect(candidateRes1.candidate).toBe(testcandidate);
expect(candidateRes1.sdpMid).toBe(1);
expect(candidateRes1.sdpMLineIndex).toBe(1);
await signaling1.sendCandidate(connectionId1, testcandidate, 1, 1);
await waitFor(() => candidateRes2 != null);
expect(candidateRes2.connectionId).toBe(connectionId1);
expect(candidateRes2.candidate).toBe(testcandidate);
expect(candidateRes2.sdpMid).toBe(1);
expect(candidateRes2.sdpMLineIndex).toBe(1);
await signaling1.deleteConnection(connectionId1);
await waitFor(() => disconnectRes1 != null);
await signaling2.deleteConnection(connectionId2);
await waitFor(() => disconnectRes2 != null);
});
});
describe.each([
{ mode: "mock" },
{ mode: "http" },
{ mode: "websocket" },
])('signaling test in private mode', ({ mode }) => {
let signaling1;
let signaling2;
const connectionId = "12345";
const testsdp = "test sdp";
const testcandidate = "test candidate";
beforeAll(async () => {
if (mode == "mock") {
reset(true);
signaling1 = new MockSignaling(1);
signaling2 = new MockSignaling(1);
return;
}
const path = Path.resolve(`../bin~/${serverExeName()}`);
let cmd = `${path} -p ${portNumber} -m private`;
if (mode == "http") {
cmd += " -t http";
}
await setup({ command: cmd, port: portNumber, usedPortAction: 'error' });
if (mode == "http") {
signaling1 = new Signaling(1);
signaling2 = new Signaling(1);
}
if (mode == "websocket") {
signaling1 = new WebSocketSignaling(1);
signaling2 = new WebSocketSignaling(1);
}
await signaling1.start();
await signaling2.start();
});
afterAll(async () => {
await signaling1.stop();
await signaling2.stop();
signaling1 = null;
signaling2 = null;
if (mode == "mock") {
return;
}
await teardown();
// work around for linux, waitng kill server process
await sleep(1000);
});
test(`onConnect using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
await signaling1.createConnection(connectionId);
await waitFor(() => connectRes1 != null);
expect(connectRes1.connectionId).toBe(connectionId);
expect(connectRes1.polite).toBe(false);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes2 != null);
expect(connectRes2.connectionId).toBe(connectionId);
expect(connectRes2.polite).toBe(true);
await sleep(signaling1.interval * 2);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onOffer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId);
await waitFor(() => connectRes1 != null);
expect(connectRes1.connectionId).toBe(connectionId);
signaling1.sendOffer(connectionId, testsdp);
await sleep(signaling1.interval * 2);
// Do not receive offer other signaling if not connected same sendoffer connectionId in private mode
expect(offerRes2).toBeUndefined();
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes2 != null);
expect(connectRes2.connectionId).toBe(connectionId);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.polite).toBe(true);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`onCandidate using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let answerRes1;
let candidateRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling1.addEventListener('candidate', (e) => candidateRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let candidateRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
signaling2.addEventListener('candidate', (e) => candidateRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
await signaling2.sendCandidate(connectionId, testcandidate, 1, 1);
await waitFor(() => candidateRes1 != null);
expect(candidateRes1.connectionId).toBe(connectionId);
expect(candidateRes1.candidate).toBe(testcandidate);
expect(candidateRes1.sdpMLineIndex).toBe(1);
expect(candidateRes1.sdpMid).toBe(1);
await signaling1.sendCandidate(connectionId, testcandidate, 1, 1);
await waitFor(() => candidateRes2 != null);
expect(candidateRes2.connectionId).toBe(connectionId);
expect(candidateRes2.candidate).toBe(testcandidate);
expect(candidateRes2.sdpMLineIndex).toBe(1);
expect(candidateRes2.sdpMid).toBe(1);
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
test(`notReceiveOwnOfferAnswer using ${mode}`, async () => {
let connectRes1;
let disconnectRes1;
let offerRes1;
let answerRes1;
signaling1.addEventListener('connect', (e) => connectRes1 = e.detail);
signaling1.addEventListener('disconnect', (e) => disconnectRes1 = e.detail);
let connectRes2;
let disconnectRes2;
let offerRes2;
let answerRes2;
signaling2.addEventListener('connect', (e) => connectRes2 = e.detail);
signaling2.addEventListener('disconnect', (e) => disconnectRes2 = e.detail);
await signaling1.createConnection(connectionId);
await signaling2.createConnection(connectionId);
await waitFor(() => connectRes1 != null && connectRes2 != null);
signaling1.addEventListener('offer', (e) => offerRes1 = e.detail);
signaling2.addEventListener('offer', (e) => offerRes2 = e.detail);
await signaling1.sendOffer(connectionId, testsdp);
await waitFor(() => offerRes2 != null);
await sleep(signaling1.interval * 2);
expect(offerRes1).toBeUndefined();
expect(offerRes2).not.toBeUndefined();
expect(offerRes2.connectionId).toBe(connectionId);
expect(offerRes2.sdp).toBe(testsdp);
signaling1.addEventListener('answer', (e) => answerRes1 = e.detail);
signaling2.addEventListener('answer', (e) => answerRes2 = e.detail);
await signaling2.sendAnswer(connectionId, testsdp);
await waitFor(() => answerRes1 != null);
await sleep(signaling2.interval * 2);
expect(answerRes1).not.toBeUndefined();
expect(answerRes1.connectionId).toBe(connectionId);
expect(answerRes1.sdp).toBe(testsdp);
expect(answerRes2).toBeUndefined();
await signaling1.deleteConnection(connectionId);
await waitFor(() => disconnectRes1 != null && disconnectRes2 != null);
expect(disconnectRes1.connectionId).toBe(connectionId);
expect(disconnectRes2.connectionId).toBe(connectionId);
disconnectRes2 = null;
await signaling2.deleteConnection(connectionId);
await waitFor(() => disconnectRes2 != null);
});
});