test: tests unitaires socketio-adapter (8 tests, bun test)

- Injection socket factory pour testabilité (2e param constructeur)
- Tests : état initial, connect, login_error, connect_error,
  reconnecting, reconnexion, logoff, onStateChange
- Script test ajouté dans package.json
This commit is contained in:
Pierre Marx
2026-03-18 20:38:57 -04:00
parent 9e683a04e9
commit cd76e71a5e
3 changed files with 181 additions and 14 deletions

131
socketio-adapter.test.js Normal file
View File

@@ -0,0 +1,131 @@
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"]);
});
});