mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
feat(e2e): complete E2E v2 suite — 66 specs, orchestrator, bug fixes (#2353)
Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
co-authored by
Steven Enamakel
Steven Enamakel
parent
9ac9613bd5
commit
03d1e2512e
@@ -185,8 +185,8 @@ function createServerInstance() {
|
||||
openSockets.add(socket);
|
||||
socket.on("close", () => openSockets.delete(socket));
|
||||
});
|
||||
nextServer.on("upgrade", (req, socket) =>
|
||||
handleWebSocketUpgrade(req, socket),
|
||||
nextServer.on("upgrade", (req, socket, head) =>
|
||||
handleWebSocketUpgrade(req, socket, head),
|
||||
);
|
||||
return nextServer;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,11 @@ import {
|
||||
} from "./protocol.mjs";
|
||||
import {
|
||||
acceptWebSocket,
|
||||
closeWebSocket,
|
||||
decodeWebSocketFrames,
|
||||
sendWsText,
|
||||
socketIsOpen,
|
||||
upgradeWebSocket,
|
||||
} from "./websocket.mjs";
|
||||
|
||||
function socketIoSid() {
|
||||
@@ -83,11 +86,7 @@ function sendSocketPacket(session, packet) {
|
||||
const target = getSocketSession(session.sid);
|
||||
if (!target) return false;
|
||||
target.lastSeenAt = new Date().toISOString();
|
||||
if (
|
||||
target.webSocket &&
|
||||
!target.webSocket.destroyed &&
|
||||
target.upgradedToWebSocket === true
|
||||
) {
|
||||
if (socketIsOpen(target.webSocket) && target.upgradedToWebSocket === true) {
|
||||
sendWsText(target.webSocket, packet);
|
||||
return true;
|
||||
}
|
||||
@@ -97,13 +96,8 @@ function sendSocketPacket(session, packet) {
|
||||
function cleanupRejectedSession(session) {
|
||||
const live = getSocketSession(session.sid);
|
||||
if (!live) return;
|
||||
if (live.webSocket && !live.webSocket.destroyed) {
|
||||
try {
|
||||
live.webSocket.end?.();
|
||||
live.webSocket.destroy?.();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
if (socketIsOpen(live.webSocket)) {
|
||||
closeWebSocket(live.webSocket);
|
||||
dropSocketSession(live.sid);
|
||||
return;
|
||||
}
|
||||
@@ -329,14 +323,7 @@ export function handleSocketRequest(ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function handleWebSocketUpgrade(req, socket) {
|
||||
if (!req.url?.startsWith("/socket.io/")) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!acceptWebSocket(req, socket)) return;
|
||||
|
||||
function attachAcceptedWebSocket(req, socket) {
|
||||
const urlObj = parseRequestUrl(req.url);
|
||||
const requestedSid = urlObj.searchParams.get("sid");
|
||||
let session = requestedSid ? getSocketSession(requestedSid) : null;
|
||||
@@ -373,6 +360,24 @@ export function handleWebSocketUpgrade(req, socket) {
|
||||
socket.on("error", () => {});
|
||||
}
|
||||
|
||||
export function handleWebSocketUpgrade(req, socket, head) {
|
||||
if (!req.url?.startsWith("/socket.io/")) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
upgradeWebSocket(req, socket, head, (ws) =>
|
||||
attachAcceptedWebSocket(req, ws),
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!acceptWebSocket(req, socket)) return;
|
||||
attachAcceptedWebSocket(req, socket);
|
||||
}
|
||||
|
||||
export function emitMockSocketEvent({
|
||||
event,
|
||||
data,
|
||||
@@ -489,12 +494,7 @@ export function disconnectMockSockets({ targetSid, targetUserId } = {}) {
|
||||
if (!matchSession(sessionInfo, { targetSid, targetUserId })) continue;
|
||||
const session = getSocketSession(sessionInfo.sid);
|
||||
if (!session) continue;
|
||||
try {
|
||||
session.webSocket?.end?.();
|
||||
session.webSocket?.destroy?.();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
closeWebSocket(session.webSocket);
|
||||
dropSocketSession(session.sid);
|
||||
disconnected += 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
import crypto from "node:crypto";
|
||||
import { WebSocketServer } from "ws";
|
||||
|
||||
const wsServer = new WebSocketServer({ noServer: true });
|
||||
|
||||
function isWsLibrarySocket(socket) {
|
||||
return socket && typeof socket.send === "function";
|
||||
}
|
||||
|
||||
export function socketIsOpen(socket) {
|
||||
if (!socket) return false;
|
||||
if (isWsLibrarySocket(socket)) return socket.readyState === 1;
|
||||
return !socket.destroyed;
|
||||
}
|
||||
|
||||
export function closeWebSocket(socket) {
|
||||
if (!socket) return;
|
||||
try {
|
||||
if (isWsLibrarySocket(socket)) {
|
||||
socket.close();
|
||||
socket.terminate?.();
|
||||
return;
|
||||
}
|
||||
socket.end?.();
|
||||
socket.destroy?.();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export function sendWsFrame(socket, opcode, payload) {
|
||||
if (!socket || socket.destroyed) return;
|
||||
@@ -30,11 +58,24 @@ export function sendWsFrame(socket, opcode, payload) {
|
||||
}
|
||||
|
||||
export function sendWsText(socket, text) {
|
||||
if (isWsLibrarySocket(socket)) {
|
||||
if (socket.readyState === 1) socket.send(String(text));
|
||||
return;
|
||||
}
|
||||
sendWsFrame(socket, 0x01, Buffer.from(text, "utf-8"));
|
||||
}
|
||||
|
||||
export function upgradeWebSocket(req, socket, head, onConnection) {
|
||||
if (!Buffer.isBuffer(head)) return false;
|
||||
wsServer.handleUpgrade(req, socket, head, (ws) => {
|
||||
onConnection(ws);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export function acceptWebSocket(req, socket) {
|
||||
const key = req.headers["sec-websocket-key"];
|
||||
const rawKey = req.headers["sec-websocket-key"];
|
||||
const key = Array.isArray(rawKey) ? rawKey[0]?.trim() : rawKey?.trim();
|
||||
if (!key) {
|
||||
socket.destroy();
|
||||
return false;
|
||||
@@ -54,6 +95,13 @@ export function acceptWebSocket(req, socket) {
|
||||
}
|
||||
|
||||
export function decodeWebSocketFrames(socket, onText) {
|
||||
if (isWsLibrarySocket(socket)) {
|
||||
socket.on("message", (data) => {
|
||||
onText(Buffer.isBuffer(data) ? data.toString("utf-8") : String(data));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer = Buffer.alloc(0);
|
||||
|
||||
socket.on("data", (chunk) => {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
export const DEFAULT_PORT = 18473;
|
||||
export const MOCK_JWT = "e2e-mock-jwt-token";
|
||||
// Valid JWT format so isPlausibleSessionToken() in CoreStateProvider
|
||||
// recognizes it and triggers the auth-refresh path (clears logoutGuard).
|
||||
// exp = 4102444800 ≈ year 2099 — effectively never expires in tests.
|
||||
export const MOCK_JWT =
|
||||
"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0" +
|
||||
".eyJzdWIiOiJ1c2VyLTEyMyIsInVzZXJJZCI6InVzZXItMTIzIiwidGdVc2VySWQiOiJ1c2VyLTEyMyIsImV4cCI6NDEwMjQ0NDgwMH0" +
|
||||
".e2e";
|
||||
export const MAX_PORT_RETRY_ATTEMPTS = 10;
|
||||
export const MAX_MOCK_DELAY_MS = 30_000;
|
||||
|
||||
@@ -330,6 +336,8 @@ export function dropSocketSession(sid) {
|
||||
const session = getSocketSession(sid);
|
||||
if (!session) return;
|
||||
try {
|
||||
session.webSocket?.close?.();
|
||||
session.webSocket?.terminate?.();
|
||||
session.webSocket?.destroy?.();
|
||||
} catch {
|
||||
// noop
|
||||
|
||||
Reference in New Issue
Block a user