import type { AddressInfo } from "node:net" import { afterEach, expect, test, vi } from "vitest" import { WebSocket as WsClient } from "ws" import { Server } from "#Source/socket/node.index.ts" afterEach(() => { vi.restoreAllMocks() }) test("Server.createWebSocketServer returns usable local URLs and stable lifecycle helpers", async () => { const webSocketServer = Server.createWebSocketServer({ port: 0 }) expect(webSocketServer.isRunning()).toBe(false) const startResult = await webSocketServer.start() const address = webSocketServer.getServer().address() as AddressInfo expect(startResult.urlList).toContain(`ws://127.0.0.1:${address.port}`) expect(startResult.urlList).toContain(`ws://localhost:${address.port}`) expect(startResult.urlList).toContain(`ws://[::1]:${address.port}`) expect(webSocketServer.isRunning()).toBe(true) await webSocketServer.close() expect(webSocketServer.isRunning()).toBe(false) }) test("Server.Socket attach does not take over an already running server lifecycle", async () => { const webSocketServer = Server.createWebSocketServer({ port: 0 }) await webSocketServer.start() const socket = new Server.Socket<{ type: string }>({ webSocketServer, onMessage: () => { // no op }, }) socket.attach() expect(socket.getSnapshot()).toMatchObject({ isRunning: true, isStarted: true, managesWebSocketServerLifecycle: false, }) await socket.close() expect(webSocketServer.isRunning()).toBe(true) await webSocketServer.close() }) test("Server.Socket exposes stable connect timing and forwards messages for active connections", async () => { const webSocketServer = Server.createWebSocketServer({ port: 0 }) const socket = new Server.Socket<{ type: string }>({ webSocketServer, onMessage: () => { // no-op }, }) const connectPromise = new Promise((resolve) => { socket.eventManager.subscribe("connect", ({ clientId }) => { const socketUnitSnapshot = socket.getSocketUnitSnapshot(clientId) expect(socketUnitSnapshot).toMatchObject({ clientId, isReadyToWork: true, status: "OPEN", }) resolve(clientId) }) }) const inboundMessagePromise = new Promise<{ clientId: string; message: { type: string } }>( (resolve) => { socket.eventManager.subscribe("message", (payload) => { resolve(payload) }) }, ) const closePromise = new Promise((resolve) => { socket.eventManager.subscribe("close", ({ clientId }) => { resolve(clientId) }) }) await socket.open() const address = webSocketServer.getServer().address() as AddressInfo const client = new WsClient(`ws://127.0.0.1:${address.port}`) await new Promise((resolve, reject) => { client.once("open", () => { resolve() }) client.once("error", reject) }) const clientId = await connectPromise client.send(JSON.stringify({ type: "hello-server" })) await expect(inboundMessagePromise).resolves.toEqual({ clientId, message: { type: "hello-server" }, }) const outboundMessagePromise = new Promise((resolve, reject) => { client.once("message", (message) => { resolve(message.toString()) }) client.once("error", reject) }) socket.sendMessage(clientId, { type: "hello-client" }) await expect(outboundMessagePromise).resolves.toBe(JSON.stringify({ type: "hello-client" })) client.close() await expect(closePromise).resolves.toBe(clientId) await socket.close() expect(socket.getSnapshot()).toMatchObject({ activeSocketUnitCount: 0, isRunning: false, isStarted: false, }) })