bridge: the discovery answer is published through send()

The answer on <root>/discover_r called the publisher directly; a publisher
that threw or rejected was only caught by the handler meant for listeners
of "discover". It now takes the path of every device message: send()
resolves, and a failure goes to the "error" event when somebody listens.
A test covers a publish that fails, throws and rejects, with and without
an "error" listener: receive() resolves each time and "discover" is not
emitted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 21:15:47 +00:00
parent 94b13f733a
commit d12767f9a7
4 changed files with 40 additions and 15 deletions

View file

@ -63,3 +63,29 @@ test("unregisterDevice and clearDevices take the publisher from the device", ()
bridge.clearDevices();
assert.equal(plug.publisher, null);
});
test("a discovery answer that cannot be published: receive() resolves and the \"error\" event says why", async () => {
const gone = new MemoryPublisher();
gone.failWith = new Error("broker gone");
const publishers = {
"fails": gone,
"throws": { publish() { throw new Error("publisher threw"); } },
"rejects": { publish: () => Promise.reject(new Error("publisher rejected")) },
};
const expected = { "fails": "broker gone", "throws": "publisher threw", "rejects": "publisher rejected" };
for (const [how, publisher] of Object.entries(publishers)) {
const bridge = new Alex2MQTT("user", "password", "root", false, { publisher });
bridge.addDevice({ endpointId: "lamp-1", name: "Lamp", categories: ["LIGHT"] }).add(PowerController);
const errors = [];
const discovered = [];
bridge.on("error", (err) => errors.push(err.message));
bridge.on("discover", (n) => discovered.push(n));
await bridge.receive("root/discover", "");
assert.deepEqual(errors, [expected[how]], how);
assert.deepEqual(discovered, [], how);
// Without a listener the error has nowhere to go, and still nothing is thrown or rejected
bridge.removeAllListeners("error");
await bridge.receive("root/discover", "");
}
});