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:
YellowSnnowmann
2026-05-22 17:23:01 -07:00
committed by GitHub
co-authored by Steven Enamakel Steven Enamakel
parent 9ac9613bd5
commit 03d1e2512e
79 changed files with 4757 additions and 1126 deletions
+2 -2
View File
@@ -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 -26
View File
@@ -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;
}
+49 -1
View File
@@ -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) => {
+9 -1
View File
@@ -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