// E2E Websocket Open tests cover e2e websocket open script behavior.
import { EventEmitter } from "node:events";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { waitForWebSocketOpen } from "../../scripts/e2e/lib/websocket-open.mjs";

class FakeWebSocket extends EventEmitter {
  terminated = false;

  terminate(): void {
    this.terminated = true;
    queueMicrotask(() => {
      this.emit("error", new Error("socket abort after terminate"));
      this.emit("close");
    });
  }
}

describe("E2E WebSocket open guard", () => {
  beforeEach(() => {
    vi.useRealTimers();
  });

  it("consumes abort errors after open timeouts", async () => {
    const ws = new FakeWebSocket();
    const keepAlive = setTimeout(() => {}, 100);

    try {
      await expect(waitForWebSocketOpen(ws, 1)).rejects.toThrow("ws open timeout");
    } finally {
      clearTimeout(keepAlive);
    }
    await new Promise((resolve) => {
      setImmediate(resolve);
    });

    expect(ws.terminated).toBe(true);
    expect(ws.listenerCount("open")).toBe(0);
    expect(ws.listenerCount("error")).toBe(0);
    expect(ws.listenerCount("close")).toBe(0);
  });

  it("uses caller-specific timeout messages", async () => {
    const ws = new FakeWebSocket();
    const keepAlive = setTimeout(() => {}, 100);

    try {
      await expect(waitForWebSocketOpen(ws, 1, "gateway ws open timeout")).rejects.toThrow(
        "gateway ws open timeout",
      );
    } finally {
      clearTimeout(keepAlive);
    }
  });

  it("cleans listeners after successful opens", async () => {
    const ws = new FakeWebSocket();
    const opened = waitForWebSocketOpen(ws, 100);

    ws.emit("open");

    await expect(opened).resolves.toBeUndefined();
    expect(ws.terminated).toBe(false);
    expect(ws.listenerCount("open")).toBe(0);
    expect(ws.listenerCount("error")).toBe(0);
    expect(ws.listenerCount("close")).toBe(0);
  });

  it("rejects immediately when the socket closes before opening", async () => {
    const ws = new FakeWebSocket();
    const opened = waitForWebSocketOpen(ws, 1000);

    ws.emit("close", 1006, Buffer.from("bye"));

    await expect(opened).rejects.toThrow("closed before open: 1006 bye");
    expect(ws.terminated).toBe(false);
    expect(ws.listenerCount("open")).toBe(0);
    expect(ws.listenerCount("error")).toBe(0);
    expect(ws.listenerCount("close")).toBe(0);
  });
});
