mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
Give an error response back to unhandled requests, this fixes requests hanging forever when the target does not support the given request.
188 lines
6.4 KiB
JavaScript
188 lines
6.4 KiB
JavaScript
"use strict";
|
|
const assert = require("assert").strict;
|
|
const events = require("events");
|
|
|
|
const libLink = require("@clusterio/lib/link");
|
|
const libErrors = require("@clusterio/lib/errors");
|
|
const libSchema = require("@clusterio/lib/schema");
|
|
const mock = require("../../mock");
|
|
|
|
|
|
describe("lib/link/link", function() {
|
|
describe("class Link", function() {
|
|
let testConnector;
|
|
let testLink;
|
|
|
|
before(function() {
|
|
testConnector = new mock.MockConnector();
|
|
testLink = new libLink.Link("source", "target", testConnector);
|
|
testLink.setValidator("simple", libSchema.compile({
|
|
properties: {
|
|
"data": {
|
|
type: "object",
|
|
properties: {
|
|
"string": { type: "string" },
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
});
|
|
|
|
it("should emitt error processing message to connector", async function() {
|
|
testLink.setHandler("throws", () => { throw new Error("Handler error"); }, () => true);
|
|
let result = events.once(testConnector, "message");
|
|
testConnector.emit("message", { seq: 1, type: "throws", data: {}});
|
|
await assert.rejects(result, new Error("Handler error"));
|
|
});
|
|
|
|
it("should give an error response back on an unhandled request", function() {
|
|
testConnector.sentMessages = [];
|
|
testConnector.emit("message", { seq: 1, type: "unhandled_request", data: {}});
|
|
assert.deepEqual(testConnector.sentMessages, [{
|
|
type: "unhandled_response",
|
|
seq: testConnector._seq - 1,
|
|
data: { seq: 1, error: "No validator for unhandled_request on source-target" } },
|
|
])
|
|
});
|
|
|
|
describe(".setValidator()", function() {
|
|
it("should set the validator", function() {
|
|
testLink.setValidator("test", () => true);
|
|
assert(testLink._validators.has("test"), "validator was not added");
|
|
});
|
|
it("should throw on duplicated validator", function() {
|
|
assert.throws(
|
|
() => testLink.setValidator("test", () => true),
|
|
new Error("test already has a validator")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe(".setHandler()", function() {
|
|
it("should throw if validator is not passed", function() {
|
|
assert.throws(
|
|
() => testLink.setHandler("no_validator_test", (message) => {}),
|
|
new Error("validator is required")
|
|
);
|
|
});
|
|
it("should set the handler", function() {
|
|
function handler(message) { }
|
|
testLink.setHandler("handler_test", handler, (message) => true);
|
|
assert.equal(testLink._handlers.get("handler_test"), handler);
|
|
});
|
|
it("should throw on duplicated handler", function() {
|
|
assert.throws(
|
|
() => testLink.setHandler("handler_test", (message) => {}),
|
|
new Error("handler_test already has a handler")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe(".processMessage()", function() {
|
|
it("should throw on invalid message", function() {
|
|
assert.throws(
|
|
() => testLink.processMessage({ data: "invalid message" }),
|
|
{ message: "Malformed" }
|
|
);
|
|
});
|
|
it("should throw on message without validator", function() {
|
|
assert.throws(
|
|
() => testLink.processMessage({ seq: 1, type: "no_validator", data: {} }),
|
|
new libErrors.InvalidMessage("No validator for no_validator on source-target")
|
|
);
|
|
});
|
|
it("should throw on message failing validation", function() {
|
|
assert.throws(
|
|
() => testLink.processMessage({ seq: 1, type: "simple", data: { string: 1 }}),
|
|
{ message: "Validation failed for simple" }
|
|
);
|
|
});
|
|
it("should throw on unhandled message", function() {
|
|
assert.throws(
|
|
() => testLink.processMessage({ seq: 1, type: "simple", data: { string: "a" }}),
|
|
new libErrors.InvalidMessage("Unhandled message simple")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("._processHandler()", function() {
|
|
it("should call the handler", function() {
|
|
let handled = [];
|
|
testLink.setHandler("handled", (message) => handled.push(message), (message) => true);
|
|
testLink._processHandler({ seq: 1, type: "handled", data: { test: "foo" } });
|
|
assert.deepEqual(handled, [{ seq: 1, type: "handled", data: { test: "foo" } }]);
|
|
});
|
|
it("should return true if handled", function() {
|
|
let result = testLink._processHandler({ seq: 1, type: "handled", data: {} });
|
|
assert.equal(result, true);
|
|
});
|
|
it("should return false if not handled", function() {
|
|
let result = testLink._processHandler({ seq: 1, type: "not_handled", data: {} });
|
|
assert.equal(result, false);
|
|
});
|
|
});
|
|
|
|
describe("._processWaiters()", function() {
|
|
it("should call waiters", async function() {
|
|
let waiter = new Promise(resolve => {
|
|
testLink._waiters.set("test", [{ resolve, data: {} }]);
|
|
});
|
|
assert(
|
|
testLink._processWaiters({ type: "test", seq: 4, data: {} }),
|
|
"Waiter was not processed"
|
|
);
|
|
assert.deepEqual(await waiter, { type: "test", seq: 4, data: {} });
|
|
});
|
|
it("should filter on data", async function() {
|
|
let waiter = new Promise(resolve => {
|
|
testLink._waiters.set("test", [{ resolve, data: { a: 1, b: 2 } }]);
|
|
});
|
|
assert(
|
|
!testLink._processWaiters({ type: "test", seq: 4, data: {} }),
|
|
"Incorrectly matched empty message"
|
|
);
|
|
assert(
|
|
!testLink._processWaiters({ type: "test", seq: 4, data: { a: 1, b: 0 } }),
|
|
"Incorrectly matched message with wrong data"
|
|
);
|
|
assert(
|
|
testLink._processWaiters({ type: "test", seq: 4, data: { a: 1, b: 2, c: 3 } }),
|
|
"Did not match correct message"
|
|
);
|
|
assert.deepEqual(await waiter, { type: "test", seq: 4, data: { a: 1, b: 2, c: 3 } });
|
|
});
|
|
});
|
|
|
|
describe(".waitFor()", function() {
|
|
before(function() {
|
|
testLink.setValidator("waiter", (message) => true);
|
|
});
|
|
|
|
it("should throw on missing validator", async function() {
|
|
await assert.rejects(
|
|
testLink.waitFor("no_validator", {}),
|
|
new Error("No validator for no_validator on source-target")
|
|
);
|
|
});
|
|
it("should wait for a given message", async function() {
|
|
let result = testLink.waitFor("waiter", {});
|
|
assert(
|
|
testLink._processWaiters({ type: "waiter", seq: 5, data: {} }),
|
|
"Did not proccess waiter"
|
|
);
|
|
assert.deepEqual(await result, { type: "waiter", seq: 5, data: {} });
|
|
});
|
|
it("should handle multiple waiters", async function() {
|
|
let result1 = testLink.waitFor("waiter", {});
|
|
let result2 = testLink.waitFor("waiter", {});
|
|
assert(
|
|
testLink._processWaiters({ type: "waiter", seq: 5, data: {} }),
|
|
"Did not proccess waiter"
|
|
);
|
|
assert.deepEqual(await result1, { type: "waiter", seq: 5, data: {} });
|
|
assert.deepEqual(await result2, { type: "waiter", seq: 5, data: {} });
|
|
});
|
|
});
|
|
});
|
|
});
|