import { describe, test, expect, mock, beforeEach } from "bun:test"; const SocketIOAdapter = require("./socketio-adapter.js"); // EventEmitter minimal pour simuler un socket.io function createFakeSocket() { const listeners = {}; const socket = { on(event, fn) { (listeners[event] ??= []).push(fn); }, once(event, fn) { const wrapper = (...args) => { socket.off(event, wrapper); fn(...args); }; (listeners[event] ??= []).push(wrapper); }, off(event, fn) { if (fn) { listeners[event] = (listeners[event] || []).filter(f => f !== fn); } else { delete listeners[event]; } }, emit: mock(() => {}), disconnect: mock(() => {}), connected: true, // Helper test : déclencher un event côté "serveur" _fire(event, ...args) { (listeners[event] || []).slice().forEach(fn => fn(...args)); }, }; return socket; } describe("SocketIOAdapter", () => { let adapter, socket; beforeEach(() => { socket = null; const factory = (url, opts) => { socket = createFakeSocket(); return socket; }; adapter = new SocketIOAdapter("http://localhost:8004", factory); }); test("état initial = disconnected", () => { expect(adapter.state).toBe("disconnected"); }); test("connect() passe en connecting puis connected sur login_ok", async () => { const states = []; adapter.onStateChange((s) => states.push(s)); const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234", firstName: "Test", lastName: "User", connList: [] }); const result = await p; expect(states).toEqual(["connecting", "connected"]); expect(adapter.state).toBe("connected"); expect(result.accessCode).toBe("1234"); }); test("connect() rejette sur login_error", async () => { const p = adapter.connect("1234", "wrong", "2001"); socket._fire("login_error", { message: "Mot de passe incorrect" }); expect(p).rejects.toThrow("Mot de passe incorrect"); expect(adapter.state).toBe("error"); }); test("connect() rejette sur connect_error", async () => { const p = adapter.connect("1234", "pass", "2001"); socket._fire("connect_error", new Error("Connection refused")); expect(p).rejects.toThrow("Connection refused"); expect(adapter.state).toBe("error"); }); test("déconnexion temporaire passe en reconnecting", async () => { const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; socket._fire("disconnect", "transport close"); expect(adapter.state).toBe("reconnecting"); }); test("reconnexion après déconnexion repasse en connected", async () => { const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; socket._fire("disconnect", "transport close"); expect(adapter.state).toBe("reconnecting"); socket._fire("login_ok", { accessCode: "1234" }); expect(adapter.state).toBe("connected"); }); test("logoff() émet logout et passe en disconnected sur logout_ok", async () => { const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; const logoffP = adapter.logoff(); socket._fire("logout_ok"); await logoffP; expect(socket.emit).toHaveBeenCalledWith("logout"); expect(adapter.state).toBe("disconnected"); }); test("onStateChange est appelé à chaque transition", async () => { const states = []; adapter.onStateChange((s) => states.push(s)); const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; socket._fire("disconnect", "transport close"); socket._fire("login_ok", { accessCode: "1234" }); expect(states).toEqual(["connecting", "connected", "reconnecting", "connected"]); }); test("on('call_event') recoit les champs snake_case du serveur", async () => { const received = []; adapter.on('call_event', (data) => received.push(data)); const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; // Le serveur envoie un call_event avec champs snake_case socket._fire("call_event", { event_code: 1, queue_name: "FILE_CENTRE_A", terminal: "2001", }); expect(received).toHaveLength(1); expect(received[0].event_code).toBe(1); expect(received[0].queue_name).toBe("FILE_CENTRE_A"); expect(received[0].terminal).toBe("2001"); }); test("on('call_event') enregistré avant connect() est dispatché après connexion", async () => { const received = []; // Enregistrer le handler AVANT connect (comme le fait main.js) adapter.on('call_event', (data) => received.push(data)); const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; // Le serveur envoie un call_event socket._fire("call_event", { event_code: 2, queue_name: "FILE_CENTRE_B", terminal: "2001", }); expect(received).toHaveLength(1); expect(received[0].event_code).toBe(2); expect(received[0].queue_name).toBe("FILE_CENTRE_B"); }); test("off('call_event') supprime le handler", async () => { const received = []; adapter.on('call_event', (data) => received.push(data)); const p = adapter.connect("1234", "pass", "2001"); socket._fire("login_ok", { accessCode: "1234" }); await p; // Supprimer le handler adapter.off('call_event'); // L'event ne devrait plus être capté socket._fire("call_event", { event_code: 1, queue_name: "FILE", terminal: "2001" }); expect(received).toHaveLength(0); }); });