Files
Aether/aether-vscodex/test/composite-relay.test.js
T

106 lines
3.9 KiB
JavaScript

"use strict";
const assert = require("node:assert/strict");
const test = require("node:test");
const { CompositeRelayTransport } = require("../vscode-extension/dist/compositeRelay.js");
class FakeRelay {
constructor({ connectError } = {}) {
this.connectError = connectError;
this.frames = [];
this.closed = false;
this.listeners = { message: new Set(), open: new Set(), close: new Set() };
}
async connect() {
if (this.connectError) throw this.connectError;
for (const listener of this.listeners.open) listener();
}
send(frame) { this.frames.push(frame); }
close() { this.closed = true; }
onMessage(listener) { return this.add("message", listener); }
onOpen(listener) { return this.add("open", listener); }
onClose(listener) { return this.add("close", listener); }
add(type, listener) {
this.listeners[type].add(listener);
return { dispose: () => this.listeners[type].delete(listener) };
}
receive(frame) { for (const listener of this.listeners.message) listener(frame); }
disconnect(error) { for (const listener of this.listeners.close) listener(error); }
}
test("CompositeRelayTransport keeps local control available when optional cloud connect fails", async () => {
const local = new FakeRelay();
const cloud = new FakeRelay({ connectError: new Error("cloud offline") });
const relay = new CompositeRelayTransport([
{ id: "local", transport: local, required: true },
{ id: "cloud", transport: cloud },
]);
await relay.connect();
assert.equal(relay.isConnected("local"), true);
assert.equal(relay.isConnected("cloud"), false);
relay.send({ kind: "event", type: "session.snapshot" });
assert.equal(local.frames.length, 1);
assert.equal(cloud.frames.length, 1, "optional transport may queue events for reconnect");
relay.close();
assert.equal(local.closed, true);
assert.equal(cloud.closed, true);
});
test("CompositeRelayTransport forwards commands and reports offline only after every relay closes", async () => {
const local = new FakeRelay();
const cloud = new FakeRelay();
const relay = new CompositeRelayTransport([
{ id: "local", transport: local, required: true },
{ id: "cloud", transport: cloud },
]);
const messages = [];
const closes = [];
relay.onMessage((frame) => messages.push(frame));
relay.onClose((error) => closes.push(error?.message));
await relay.connect();
assert.equal(relay.isConnected("local"), true);
assert.equal(relay.isConnected("cloud"), true);
cloud.receive({ kind: "command", type: "turn.start" });
assert.equal(messages.length, 1);
local.disconnect(new Error("local offline"));
assert.equal(relay.isConnected("local"), false);
assert.deepEqual(closes, []);
cloud.disconnect(new Error("cloud offline"));
assert.deepEqual(closes, ["cloud offline"]);
relay.close();
});
test("CompositeRelayTransport surfaces each member reconnect for snapshot hydration", async () => {
const local = new FakeRelay();
const cloud = new FakeRelay();
const relay = new CompositeRelayTransport([
{ id: "local", transport: local, required: true },
{ id: "cloud", transport: cloud },
]);
let opens = 0;
relay.onOpen(() => { opens += 1; });
await relay.connect();
assert.equal(opens, 2);
cloud.disconnect(new Error("cloud offline"));
for (const listener of cloud.listeners.open) listener();
assert.equal(opens, 3, "cloud recovery must prompt RelayHost to publish a fresh snapshot");
relay.close();
});
test("CompositeRelayTransport fails when the required local relay cannot connect", async () => {
const local = new FakeRelay({ connectError: new Error("local offline") });
const cloud = new FakeRelay();
const relay = new CompositeRelayTransport([
{ id: "local", transport: local, required: true },
{ id: "cloud", transport: cloud },
]);
await assert.rejects(relay.connect(), /local: local offline/);
assert.equal(local.closed, true);
assert.equal(cloud.closed, true);
});